mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-29 15:51:14 +00:00
Merge branch 'LemmyNet:main' into multiple-images-upload
This commit is contained in:
commit
059dfbd3a5
64 changed files with 4220 additions and 3787 deletions
|
@ -8,7 +8,8 @@
|
||||||
],
|
],
|
||||||
"extends": [
|
"extends": [
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
"plugin:@typescript-eslint/recommended"
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:inferno/recommended"
|
||||||
],
|
],
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 7ac48ae98271b3b573e28c90b87f9704492e0b62
|
Subproject commit ae3132fef13542ab7fc337361bf484183d12d786
|
21
package.json
21
package.json
|
@ -25,13 +25,13 @@
|
||||||
"emoji-short-name": "^2.0.0",
|
"emoji-short-name": "^2.0.0",
|
||||||
"express": "~4.18.1",
|
"express": "~4.18.1",
|
||||||
"i18next": "^21.8.14",
|
"i18next": "^21.8.14",
|
||||||
"inferno": "^7.4.11",
|
"inferno": "^8.0.3",
|
||||||
"inferno-create-element": "^7.4.11",
|
"inferno-create-element": "^8.0.3",
|
||||||
"inferno-helmet": "^5.2.1",
|
"inferno-helmet": "^5.2.1",
|
||||||
"inferno-hydrate": "^7.4.11",
|
"inferno-hydrate": "^8.0.3",
|
||||||
"inferno-i18next-dess": "^0.0.1",
|
"inferno-i18next-dess": "0.0.2",
|
||||||
"inferno-router": "^7.4.11",
|
"inferno-router": "^8.0.3",
|
||||||
"inferno-server": "^7.4.11",
|
"inferno-server": "^8.0.3",
|
||||||
"isomorphic-cookie": "^1.2.4",
|
"isomorphic-cookie": "^1.2.4",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"markdown-it": "^13.0.1",
|
"markdown-it": "^13.0.1",
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
"@babel/plugin-proposal-decorators": "^7.18.9",
|
"@babel/plugin-proposal-decorators": "^7.18.9",
|
||||||
"@babel/plugin-transform-runtime": "^7.18.9",
|
"@babel/plugin-transform-runtime": "^7.18.9",
|
||||||
"@babel/plugin-transform-typescript": "^7.18.8",
|
"@babel/plugin-transform-typescript": "^7.18.8",
|
||||||
"@babel/preset-env": "7.18.9",
|
"@babel/preset-env": "7.19.3",
|
||||||
"@babel/preset-typescript": "^7.18.6",
|
"@babel/preset-typescript": "^7.18.6",
|
||||||
"@babel/runtime": "^7.18.9",
|
"@babel/runtime": "^7.18.9",
|
||||||
"@sniptt/monads": "^0.5.10",
|
"@sniptt/monads": "^0.5.10",
|
||||||
|
@ -74,10 +74,11 @@
|
||||||
"copy-webpack-plugin": "^11.0.0",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"css-loader": "^6.7.1",
|
"css-loader": "^6.7.1",
|
||||||
"eslint": "^8.20.0",
|
"eslint": "^8.20.0",
|
||||||
|
"eslint-plugin-inferno": "^7.31.8",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"husky": "^8.0.1",
|
"husky": "^8.0.1",
|
||||||
"import-sort-style-module": "^6.0.0",
|
"import-sort-style-module": "^6.0.0",
|
||||||
"lemmy-js-client": "0.17.0-rc.43",
|
"lemmy-js-client": "0.17.0-rc.46",
|
||||||
"lint-staged": "^13.0.3",
|
"lint-staged": "^13.0.3",
|
||||||
"mini-css-extract-plugin": "^2.6.1",
|
"mini-css-extract-plugin": "^2.6.1",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
|
@ -92,10 +93,10 @@
|
||||||
"sortpack": "^2.3.0",
|
"sortpack": "^2.3.0",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
"terser": "^5.14.2",
|
"terser": "^5.14.2",
|
||||||
"typescript": "^4.7.4",
|
"typescript": "^4.8.4",
|
||||||
"webpack": "5.74.0",
|
"webpack": "5.74.0",
|
||||||
"webpack-cli": "^4.10.0",
|
"webpack-cli": "^4.10.0",
|
||||||
"webpack-dev-server": "4.9.3",
|
"webpack-dev-server": "4.11.1",
|
||||||
"webpack-node-externals": "^3.0.0"
|
"webpack-node-externals": "^3.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
|
@ -53,7 +53,7 @@ section {
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
margin:0;
|
margin:0;
|
||||||
font-family:Lato,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
|
font-family:Lato,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Cantarell,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
|
||||||
font-size:.9375rem;
|
font-size:.9375rem;
|
||||||
font-weight:400;
|
font-weight:400;
|
||||||
line-height:1.5;
|
line-height:1.5;
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -42,7 +42,7 @@ export class App extends Component<any, any> {
|
||||||
none: <></>,
|
none: <></>,
|
||||||
})}
|
})}
|
||||||
<Navbar siteRes={siteRes} />
|
<Navbar siteRes={siteRes} />
|
||||||
<div class="mt-4 p-0 fl-1">
|
<div className="mt-4 p-0 fl-1">
|
||||||
<Switch>
|
<Switch>
|
||||||
{routes.map(({ path, exact, component: C, ...rest }) => (
|
{routes.map(({ path, exact, component: C, ...rest }) => (
|
||||||
<Route
|
<Route
|
||||||
|
|
|
@ -16,16 +16,16 @@ export class Footer extends Component<FooterProps, any> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<nav class="container navbar navbar-expand-md navbar-light navbar-bg p-3">
|
<nav className="container navbar navbar-expand-md navbar-light navbar-bg p-3">
|
||||||
<div className="navbar-collapse">
|
<div className="navbar-collapse">
|
||||||
<ul class="navbar-nav ml-auto">
|
<ul className="navbar-nav ml-auto">
|
||||||
{this.props.site.version !== VERSION && (
|
{this.props.site.version !== VERSION && (
|
||||||
<li class="nav-item">
|
<li className="nav-item">
|
||||||
<span class="nav-link">UI: {VERSION}</span>
|
<span className="nav-link">UI: {VERSION}</span>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
<li class="nav-item">
|
<li className="nav-item">
|
||||||
<span class="nav-link">BE: {this.props.site.version}</span>
|
<span className="nav-link">BE: {this.props.site.version}</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink className="nav-link" to="/modlog">
|
<NavLink className="nav-link" to="/modlog">
|
||||||
|
@ -42,23 +42,23 @@ export class Footer extends Component<FooterProps, any> {
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
{this.props.site.federated_instances && (
|
{this.props.site.federated_instances && (
|
||||||
<li class="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink className="nav-link" to="/instances">
|
<NavLink className="nav-link" to="/instances">
|
||||||
{i18n.t("instances")}
|
{i18n.t("instances")}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
<li class="nav-item">
|
<li className="nav-item">
|
||||||
<a className="nav-link" href={docsUrl}>
|
<a className="nav-link" href={docsUrl}>
|
||||||
{i18n.t("docs")}
|
{i18n.t("docs")}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li className="nav-item">
|
||||||
<a className="nav-link" href={repoUrl}>
|
<a className="nav-link" href={repoUrl}>
|
||||||
{i18n.t("code")}
|
{i18n.t("code")}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li className="nav-item">
|
||||||
<a className="nav-link" href={joinLemmyUrl}>
|
<a className="nav-link" href={joinLemmyUrl}>
|
||||||
{i18n.t("join_lemmy")}
|
{i18n.t("join_lemmy")}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { None, Some } from "@sniptt/monads";
|
import { None } from "@sniptt/monads";
|
||||||
import { Component, createRef, linkEvent, RefObject } from "inferno";
|
import { Component, createRef, linkEvent, RefObject } from "inferno";
|
||||||
import { NavLink } from "inferno-router";
|
import { NavLink } from "inferno-router";
|
||||||
import {
|
import {
|
||||||
|
@ -21,6 +21,7 @@ import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
amAdmin,
|
amAdmin,
|
||||||
auth,
|
auth,
|
||||||
|
canCreateCommunity,
|
||||||
donateLemmyUrl,
|
donateLemmyUrl,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
notifyComment,
|
notifyComment,
|
||||||
|
@ -144,8 +145,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
// TODO class active corresponding to current page
|
// TODO class active corresponding to current page
|
||||||
navbar() {
|
navbar() {
|
||||||
return (
|
return (
|
||||||
<nav class="navbar navbar-expand-md navbar-light shadow-sm p-0 px-3">
|
<nav className="navbar navbar-expand-md navbar-light shadow-sm p-0 px-3">
|
||||||
<div class="container">
|
<div className="container">
|
||||||
{this.props.siteRes.site_view.match({
|
{this.props.siteRes.site_view.match({
|
||||||
some: siteView => (
|
some: siteView => (
|
||||||
<NavLink
|
<NavLink
|
||||||
|
@ -166,7 +167,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
})}
|
})}
|
||||||
{UserService.Instance.myUserInfo.isSome() && (
|
{UserService.Instance.myUserInfo.isSome() && (
|
||||||
<>
|
<>
|
||||||
<ul class="navbar-nav ml-auto">
|
<ul className="navbar-nav ml-auto">
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/inbox"
|
to="/inbox"
|
||||||
|
@ -179,7 +180,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
>
|
>
|
||||||
<Icon icon="bell" />
|
<Icon icon="bell" />
|
||||||
{this.state.unreadInboxCount > 0 && (
|
{this.state.unreadInboxCount > 0 && (
|
||||||
<span class="mx-1 badge badge-light">
|
<span className="mx-1 badge badge-light">
|
||||||
{numToSI(this.state.unreadInboxCount)}
|
{numToSI(this.state.unreadInboxCount)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
@ -187,7 +188,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
{this.moderatesSomething && (
|
{this.moderatesSomething && (
|
||||||
<ul class="navbar-nav ml-1">
|
<ul className="navbar-nav ml-1">
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/reports"
|
to="/reports"
|
||||||
|
@ -200,7 +201,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
>
|
>
|
||||||
<Icon icon="shield" />
|
<Icon icon="shield" />
|
||||||
{this.state.unreadReportCount > 0 && (
|
{this.state.unreadReportCount > 0 && (
|
||||||
<span class="mx-1 badge badge-light">
|
<span className="mx-1 badge badge-light">
|
||||||
{numToSI(this.state.unreadReportCount)}
|
{numToSI(this.state.unreadReportCount)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
@ -208,8 +209,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
{this.amAdmin && (
|
{amAdmin() && (
|
||||||
<ul class="navbar-nav ml-1">
|
<ul className="navbar-nav ml-1">
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/registration_applications"
|
to="/registration_applications"
|
||||||
|
@ -224,7 +225,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
>
|
>
|
||||||
<Icon icon="clipboard" />
|
<Icon icon="clipboard" />
|
||||||
{this.state.unreadApplicationCount > 0 && (
|
{this.state.unreadApplicationCount > 0 && (
|
||||||
<span class="mx-1 badge badge-light">
|
<span className="mx-1 badge badge-light">
|
||||||
{numToSI(this.state.unreadApplicationCount)}
|
{numToSI(this.state.unreadApplicationCount)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
@ -235,7 +236,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
class="navbar-toggler border-0 p-1"
|
className="navbar-toggler border-0 p-1"
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="menu"
|
aria-label="menu"
|
||||||
onClick={linkEvent(this, this.handleToggleExpandNavbar)}
|
onClick={linkEvent(this, this.handleToggleExpandNavbar)}
|
||||||
|
@ -246,8 +247,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
<div
|
<div
|
||||||
className={`${!this.state.expanded && "collapse"} navbar-collapse`}
|
className={`${!this.state.expanded && "collapse"} navbar-collapse`}
|
||||||
>
|
>
|
||||||
<ul class="navbar-nav my-2 mr-auto">
|
<ul className="navbar-nav my-2 mr-auto">
|
||||||
<li class="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/communities"
|
to="/communities"
|
||||||
className="nav-link"
|
className="nav-link"
|
||||||
|
@ -257,11 +258,15 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
{i18n.t("communities")}
|
{i18n.t("communities")}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li className="nav-item">
|
||||||
|
{/* TODO make sure this works: https://github.com/infernojs/inferno/issues/1608 */}
|
||||||
<NavLink
|
<NavLink
|
||||||
to={{
|
to={{
|
||||||
pathname: "/create_post",
|
pathname: "/create_post",
|
||||||
prevPath: this.currentLocation,
|
search: "",
|
||||||
|
hash: "",
|
||||||
|
key: "",
|
||||||
|
state: { prevPath: this.currentLocation },
|
||||||
}}
|
}}
|
||||||
className="nav-link"
|
className="nav-link"
|
||||||
onMouseUp={linkEvent(this, this.handleHideExpandNavbar)}
|
onMouseUp={linkEvent(this, this.handleHideExpandNavbar)}
|
||||||
|
@ -270,8 +275,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
{i18n.t("create_post")}
|
{i18n.t("create_post")}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
{this.canCreateCommunity && (
|
{canCreateCommunity(this.props.siteRes) && (
|
||||||
<li class="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/create_community"
|
to="/create_community"
|
||||||
className="nav-link"
|
className="nav-link"
|
||||||
|
@ -282,7 +287,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
<li class="nav-item">
|
<li className="nav-item">
|
||||||
<a
|
<a
|
||||||
className="nav-link"
|
className="nav-link"
|
||||||
title={i18n.t("support_lemmy")}
|
title={i18n.t("support_lemmy")}
|
||||||
|
@ -292,8 +297,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="navbar-nav my-2">
|
<ul className="navbar-nav my-2">
|
||||||
{this.amAdmin && (
|
{amAdmin() && (
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/admin"
|
to="/admin"
|
||||||
|
@ -310,12 +315,12 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
/^\/search/
|
/^\/search/
|
||||||
) && (
|
) && (
|
||||||
<form
|
<form
|
||||||
class="form-inline mr-2"
|
className="form-inline mr-2"
|
||||||
onSubmit={linkEvent(this, this.handleSearchSubmit)}
|
onSubmit={linkEvent(this, this.handleSearchSubmit)}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
id="search-input"
|
id="search-input"
|
||||||
class={`form-control mr-0 search-input ${
|
className={`form-control mr-0 search-input ${
|
||||||
this.state.toggleSearch ? "show-input" : "hide-input"
|
this.state.toggleSearch ? "show-input" : "hide-input"
|
||||||
}`}
|
}`}
|
||||||
onInput={linkEvent(this, this.handleSearchParam)}
|
onInput={linkEvent(this, this.handleSearchParam)}
|
||||||
|
@ -325,13 +330,13 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
placeholder={i18n.t("search")}
|
placeholder={i18n.t("search")}
|
||||||
onBlur={linkEvent(this, this.handleSearchBlur)}
|
onBlur={linkEvent(this, this.handleSearchBlur)}
|
||||||
></input>
|
></input>
|
||||||
<label class="sr-only" htmlFor="search-input">
|
<label className="sr-only" htmlFor="search-input">
|
||||||
{i18n.t("search")}
|
{i18n.t("search")}
|
||||||
</label>
|
</label>
|
||||||
<button
|
<button
|
||||||
name="search-btn"
|
name="search-btn"
|
||||||
onClick={linkEvent(this, this.handleSearchBtn)}
|
onClick={linkEvent(this, this.handleSearchBtn)}
|
||||||
class="px-1 btn btn-link"
|
className="px-1 btn btn-link"
|
||||||
style="color: var(--gray)"
|
style="color: var(--gray)"
|
||||||
aria-label={i18n.t("search")}
|
aria-label={i18n.t("search")}
|
||||||
>
|
>
|
||||||
|
@ -341,7 +346,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
)}
|
)}
|
||||||
{UserService.Instance.myUserInfo.isSome() ? (
|
{UserService.Instance.myUserInfo.isSome() ? (
|
||||||
<>
|
<>
|
||||||
<ul class="navbar-nav my-2">
|
<ul className="navbar-nav my-2">
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink
|
<NavLink
|
||||||
className="nav-link"
|
className="nav-link"
|
||||||
|
@ -354,7 +359,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
>
|
>
|
||||||
<Icon icon="bell" />
|
<Icon icon="bell" />
|
||||||
{this.state.unreadInboxCount > 0 && (
|
{this.state.unreadInboxCount > 0 && (
|
||||||
<span class="ml-1 badge badge-light">
|
<span className="ml-1 badge badge-light">
|
||||||
{numToSI(this.state.unreadInboxCount)}
|
{numToSI(this.state.unreadInboxCount)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
@ -362,7 +367,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
{this.moderatesSomething && (
|
{this.moderatesSomething && (
|
||||||
<ul class="navbar-nav my-2">
|
<ul className="navbar-nav my-2">
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink
|
<NavLink
|
||||||
className="nav-link"
|
className="nav-link"
|
||||||
|
@ -375,7 +380,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
>
|
>
|
||||||
<Icon icon="shield" />
|
<Icon icon="shield" />
|
||||||
{this.state.unreadReportCount > 0 && (
|
{this.state.unreadReportCount > 0 && (
|
||||||
<span class="ml-1 badge badge-light">
|
<span className="ml-1 badge badge-light">
|
||||||
{numToSI(this.state.unreadReportCount)}
|
{numToSI(this.state.unreadReportCount)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
@ -383,8 +388,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
{this.amAdmin && (
|
{amAdmin() && (
|
||||||
<ul class="navbar-nav my-2">
|
<ul className="navbar-nav my-2">
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/registration_applications"
|
to="/registration_applications"
|
||||||
|
@ -399,7 +404,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
>
|
>
|
||||||
<Icon icon="clipboard" />
|
<Icon icon="clipboard" />
|
||||||
{this.state.unreadApplicationCount > 0 && (
|
{this.state.unreadApplicationCount > 0 && (
|
||||||
<span class="mx-1 badge badge-light">
|
<span className="mx-1 badge badge-light">
|
||||||
{numToSI(this.state.unreadApplicationCount)}
|
{numToSI(this.state.unreadApplicationCount)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
@ -411,10 +416,10 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
.map(m => m.local_user_view.person)
|
.map(m => m.local_user_view.person)
|
||||||
.match({
|
.match({
|
||||||
some: person => (
|
some: person => (
|
||||||
<ul class="navbar-nav">
|
<ul className="navbar-nav">
|
||||||
<li class="nav-item dropdown">
|
<li className="nav-item dropdown">
|
||||||
<button
|
<button
|
||||||
class="nav-link btn btn-link dropdown-toggle"
|
className="nav-link btn btn-link dropdown-toggle"
|
||||||
onClick={linkEvent(this, this.handleToggleDropdown)}
|
onClick={linkEvent(this, this.handleToggleDropdown)}
|
||||||
id="navbarDropdown"
|
id="navbarDropdown"
|
||||||
role="button"
|
role="button"
|
||||||
|
@ -433,7 +438,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</button>
|
</button>
|
||||||
{this.state.showDropdown && (
|
{this.state.showDropdown && (
|
||||||
<div
|
<div
|
||||||
class="dropdown-content"
|
className="dropdown-content"
|
||||||
onMouseLeave={linkEvent(
|
onMouseLeave={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleToggleDropdown
|
this.handleToggleDropdown
|
||||||
|
@ -460,7 +465,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<hr class="dropdown-divider" />
|
<hr className="dropdown-divider" />
|
||||||
</li>
|
</li>
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<button
|
<button
|
||||||
|
@ -484,7 +489,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<ul class="navbar-nav my-2">
|
<ul className="navbar-nav my-2">
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/login"
|
to="/login"
|
||||||
|
@ -520,20 +525,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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.setState({ expanded: !i.state.expanded });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleHideExpandNavbar(i: Navbar) {
|
handleHideExpandNavbar(i: Navbar) {
|
||||||
|
@ -541,8 +534,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSearchParam(i: Navbar, event: any) {
|
handleSearchParam(i: Navbar, event: any) {
|
||||||
i.state.searchParam = event.target.value;
|
i.setState({ searchParam: event.target.value });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSearchSubmit(i: Navbar, event: any) {
|
handleSearchSubmit(i: Navbar, event: any) {
|
||||||
|
@ -563,8 +555,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
|
|
||||||
handleSearchBlur(i: Navbar, event: any) {
|
handleSearchBlur(i: Navbar, event: any) {
|
||||||
if (!(event.relatedTarget && event.relatedTarget.name !== "search-btn")) {
|
if (!(event.relatedTarget && event.relatedTarget.name !== "search-btn")) {
|
||||||
i.state.toggleSearch = false;
|
i.setState({ toggleSearch: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,8 +565,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleToggleDropdown(i: Navbar) {
|
handleToggleDropdown(i: Navbar) {
|
||||||
i.state.showDropdown = !i.state.showDropdown;
|
i.setState({ showDropdown: !i.state.showDropdown });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -601,25 +591,28 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
msg,
|
msg,
|
||||||
GetUnreadCountResponse
|
GetUnreadCountResponse
|
||||||
);
|
);
|
||||||
this.state.unreadInboxCount =
|
this.setState({
|
||||||
data.replies + data.mentions + data.private_messages;
|
unreadInboxCount: data.replies + data.mentions + data.private_messages,
|
||||||
this.setState(this.state);
|
});
|
||||||
this.sendUnreadCount();
|
this.sendUnreadCount();
|
||||||
} else if (op == UserOperation.GetReportCount) {
|
} else if (op == UserOperation.GetReportCount) {
|
||||||
let data = wsJsonToRes<GetReportCountResponse>(
|
let data = wsJsonToRes<GetReportCountResponse>(
|
||||||
msg,
|
msg,
|
||||||
GetReportCountResponse
|
GetReportCountResponse
|
||||||
);
|
);
|
||||||
this.state.unreadReportCount = data.post_reports + data.comment_reports;
|
this.setState({
|
||||||
this.setState(this.state);
|
unreadReportCount:
|
||||||
|
data.post_reports +
|
||||||
|
data.comment_reports +
|
||||||
|
data.private_message_reports.unwrapOr(0),
|
||||||
|
});
|
||||||
this.sendReportUnread();
|
this.sendReportUnread();
|
||||||
} else if (op == UserOperation.GetUnreadRegistrationApplicationCount) {
|
} else if (op == UserOperation.GetUnreadRegistrationApplicationCount) {
|
||||||
let data = wsJsonToRes<GetUnreadRegistrationApplicationCountResponse>(
|
let data = wsJsonToRes<GetUnreadRegistrationApplicationCountResponse>(
|
||||||
msg,
|
msg,
|
||||||
GetUnreadRegistrationApplicationCountResponse
|
GetUnreadRegistrationApplicationCountResponse
|
||||||
);
|
);
|
||||||
this.state.unreadApplicationCount = data.registration_applications;
|
this.setState({ unreadApplicationCount: data.registration_applications });
|
||||||
this.setState(this.state);
|
|
||||||
this.sendApplicationUnread();
|
this.sendApplicationUnread();
|
||||||
} else if (op == UserOperation.CreateComment) {
|
} else if (op == UserOperation.CreateComment) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
|
@ -627,8 +620,9 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
UserService.Instance.myUserInfo.match({
|
UserService.Instance.myUserInfo.match({
|
||||||
some: mui => {
|
some: mui => {
|
||||||
if (data.recipient_ids.includes(mui.local_user_view.local_user.id)) {
|
if (data.recipient_ids.includes(mui.local_user_view.local_user.id)) {
|
||||||
this.state.unreadInboxCount++;
|
this.setState({
|
||||||
this.setState(this.state);
|
unreadInboxCount: this.state.unreadInboxCount + 1,
|
||||||
|
});
|
||||||
this.sendUnreadCount();
|
this.sendUnreadCount();
|
||||||
notifyComment(data.comment_view, this.context.router);
|
notifyComment(data.comment_view, this.context.router);
|
||||||
}
|
}
|
||||||
|
@ -647,8 +641,9 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
data.private_message_view.recipient.id ==
|
data.private_message_view.recipient.id ==
|
||||||
mui.local_user_view.person.id
|
mui.local_user_view.person.id
|
||||||
) {
|
) {
|
||||||
this.state.unreadInboxCount++;
|
this.setState({
|
||||||
this.setState(this.state);
|
unreadInboxCount: this.state.unreadInboxCount + 1,
|
||||||
|
});
|
||||||
this.sendUnreadCount();
|
this.sendUnreadCount();
|
||||||
notifyPrivateMessage(
|
notifyPrivateMessage(
|
||||||
data.private_message_view,
|
data.private_message_view,
|
||||||
|
@ -677,7 +672,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.getReportCount(reportCountForm));
|
WebSocketService.Instance.send(wsClient.getReportCount(reportCountForm));
|
||||||
|
|
||||||
if (this.amAdmin) {
|
if (amAdmin()) {
|
||||||
console.log("Fetching applications...");
|
console.log("Fetching applications...");
|
||||||
|
|
||||||
let applicationCountForm = new GetUnreadRegistrationApplicationCount({
|
let applicationCountForm = new GetUnreadRegistrationApplicationCount({
|
||||||
|
|
|
@ -13,7 +13,7 @@ export class NoMatch extends Component<any, any> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
<h1>404</h1>
|
<h1>404</h1>
|
||||||
{this.errCode && (
|
{this.errCode && (
|
||||||
<h3>
|
<h3>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
CommentResponse,
|
CommentResponse,
|
||||||
CreateComment,
|
CreateComment,
|
||||||
EditComment,
|
EditComment,
|
||||||
|
Language,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
wsUserOp,
|
wsUserOp,
|
||||||
|
@ -17,6 +18,7 @@ import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
auth,
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
|
myFirstDiscussionLanguageId,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
|
@ -32,6 +34,7 @@ interface CommentFormProps {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
onReplyCancel?(): any;
|
onReplyCancel?(): any;
|
||||||
|
allLanguages: Language[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CommentFormState {
|
interface CommentFormState {
|
||||||
|
@ -74,11 +77,19 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
this.props.edit ? Some(node.comment_view.comment.content) : None,
|
this.props.edit ? Some(node.comment_view.comment.content) : None,
|
||||||
right: () => None,
|
right: () => None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let selectedLang = this.props.node
|
||||||
|
.left()
|
||||||
|
.map(n => n.comment_view.comment.language_id)
|
||||||
|
.or(myFirstDiscussionLanguageId(UserService.Instance.myUserInfo));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="mb-3">
|
<div className="mb-3">
|
||||||
{UserService.Instance.myUserInfo.isSome() ? (
|
{UserService.Instance.myUserInfo.isSome() ? (
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={initialContent}
|
initialContent={initialContent}
|
||||||
|
initialLanguageId={selectedLang}
|
||||||
|
showLanguage
|
||||||
buttonTitle={Some(this.state.buttonTitle)}
|
buttonTitle={Some(this.state.buttonTitle)}
|
||||||
maxLength={None}
|
maxLength={None}
|
||||||
finished={this.state.finished}
|
finished={this.state.finished}
|
||||||
|
@ -88,9 +99,10 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
onSubmit={this.handleCommentSubmit}
|
onSubmit={this.handleCommentSubmit}
|
||||||
onReplyCancel={this.handleReplyCancel}
|
onReplyCancel={this.handleReplyCancel}
|
||||||
placeholder={Some(i18n.t("comment_here"))}
|
placeholder={Some(i18n.t("comment_here"))}
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div class="alert alert-warning" role="alert">
|
<div className="alert alert-warning" role="alert">
|
||||||
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
||||||
<T i18nKey="must_login" class="d-inline">
|
<T i18nKey="must_login" class="d-inline">
|
||||||
#
|
#
|
||||||
|
@ -104,27 +116,34 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommentSubmit(msg: { val: string; formId: string }) {
|
handleCommentSubmit(msg: {
|
||||||
|
val: Option<string>;
|
||||||
|
formId: string;
|
||||||
|
languageId: Option<number>;
|
||||||
|
}) {
|
||||||
let content = msg.val;
|
let content = msg.val;
|
||||||
this.state.formId = Some(msg.formId);
|
let language_id = msg.languageId;
|
||||||
|
this.setState({ formId: Some(msg.formId) });
|
||||||
|
|
||||||
this.props.node.match({
|
this.props.node.match({
|
||||||
left: node => {
|
left: node => {
|
||||||
if (this.props.edit) {
|
if (this.props.edit) {
|
||||||
let form = new EditComment({
|
let form = new EditComment({
|
||||||
content: Some(content),
|
content,
|
||||||
distinguished: None,
|
distinguished: None,
|
||||||
form_id: this.state.formId,
|
form_id: this.state.formId,
|
||||||
comment_id: node.comment_view.comment.id,
|
comment_id: node.comment_view.comment.id,
|
||||||
|
language_id,
|
||||||
auth: auth().unwrap(),
|
auth: auth().unwrap(),
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.editComment(form));
|
WebSocketService.Instance.send(wsClient.editComment(form));
|
||||||
} else {
|
} else {
|
||||||
let form = new CreateComment({
|
let form = new CreateComment({
|
||||||
content,
|
content: content.unwrap(),
|
||||||
form_id: this.state.formId,
|
form_id: this.state.formId,
|
||||||
post_id: node.comment_view.post.id,
|
post_id: node.comment_view.post.id,
|
||||||
parent_id: Some(node.comment_view.comment.id),
|
parent_id: Some(node.comment_view.comment.id),
|
||||||
|
language_id,
|
||||||
auth: auth().unwrap(),
|
auth: auth().unwrap(),
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.createComment(form));
|
WebSocketService.Instance.send(wsClient.createComment(form));
|
||||||
|
@ -132,10 +151,11 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
},
|
},
|
||||||
right: postId => {
|
right: postId => {
|
||||||
let form = new CreateComment({
|
let form = new CreateComment({
|
||||||
content,
|
content: content.unwrap(),
|
||||||
form_id: this.state.formId,
|
form_id: this.state.formId,
|
||||||
post_id: postId,
|
post_id: postId,
|
||||||
parent_id: None,
|
parent_id: None,
|
||||||
|
language_id,
|
||||||
auth: auth().unwrap(),
|
auth: auth().unwrap(),
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.createComment(form));
|
WebSocketService.Instance.send(wsClient.createComment(form));
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
DeleteComment,
|
DeleteComment,
|
||||||
EditComment,
|
EditComment,
|
||||||
GetComments,
|
GetComments,
|
||||||
|
Language,
|
||||||
ListingType,
|
ListingType,
|
||||||
MarkCommentReplyAsRead,
|
MarkCommentReplyAsRead,
|
||||||
MarkPersonMentionAsRead,
|
MarkPersonMentionAsRead,
|
||||||
|
@ -101,6 +102,7 @@ interface CommentNodeProps {
|
||||||
showCommunity?: boolean;
|
showCommunity?: boolean;
|
||||||
enableDownvotes: boolean;
|
enableDownvotes: boolean;
|
||||||
viewType: CommentViewType;
|
viewType: CommentViewType;
|
||||||
|
allLanguages: Language[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
|
@ -147,13 +149,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
// TODO see if there's a better way to do this, and all willReceiveProps
|
// TODO see if there's a better way to do this, and all willReceiveProps
|
||||||
componentWillReceiveProps(nextProps: CommentNodeProps) {
|
componentWillReceiveProps(nextProps: CommentNodeProps) {
|
||||||
let cv = nextProps.node.comment_view;
|
let cv = nextProps.node.comment_view;
|
||||||
this.state.my_vote = cv.my_vote;
|
this.setState({
|
||||||
this.state.upvotes = cv.counts.upvotes;
|
my_vote: cv.my_vote,
|
||||||
this.state.downvotes = cv.counts.downvotes;
|
upvotes: cv.counts.upvotes,
|
||||||
this.state.score = cv.counts.score;
|
downvotes: cv.counts.downvotes,
|
||||||
this.state.readLoading = false;
|
score: cv.counts.score,
|
||||||
this.state.saveLoading = false;
|
readLoading: false,
|
||||||
this.setState(this.state);
|
saveLoading: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -227,10 +230,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class={`${!this.props.noIndent && this.props.node.depth && "ml-2"}`}
|
className={`${
|
||||||
|
!this.props.noIndent && this.props.node.depth && "ml-2"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<div class="d-flex flex-wrap align-items-center text-muted small">
|
<div className="d-flex flex-wrap align-items-center text-muted small">
|
||||||
<span class="mr-2">
|
<span className="mr-2">
|
||||||
<PersonListing person={cv.creator} />
|
<PersonListing person={cv.creator} />
|
||||||
</span>
|
</span>
|
||||||
{cv.comment.distinguished && (
|
{cv.comment.distinguished && (
|
||||||
|
@ -263,16 +268,16 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
)}
|
)}
|
||||||
{this.props.showCommunity && (
|
{this.props.showCommunity && (
|
||||||
<>
|
<>
|
||||||
<span class="mx-1">{i18n.t("to")}</span>
|
<span className="mx-1">{i18n.t("to")}</span>
|
||||||
<CommunityLink community={cv.community} />
|
<CommunityLink community={cv.community} />
|
||||||
<span class="mx-2">•</span>
|
<span className="mx-2">•</span>
|
||||||
<Link className="mr-2" to={`/post/${cv.post.id}`}>
|
<Link className="mr-2" to={`/post/${cv.post.id}`}>
|
||||||
{cv.post.name}
|
{cv.post.name}
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm text-muted"
|
className="btn btn-sm text-muted"
|
||||||
onClick={linkEvent(this, this.handleCommentCollapse)}
|
onClick={linkEvent(this, this.handleCommentCollapse)}
|
||||||
aria-label={this.expandText}
|
aria-label={this.expandText}
|
||||||
data-tippy-content={this.expandText}
|
data-tippy-content={this.expandText}
|
||||||
|
@ -294,7 +299,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
data-tippy-content={this.pointsTippy}
|
data-tippy-content={this.pointsTippy}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="mr-1 font-weight-bold"
|
className="mr-1 font-weight-bold"
|
||||||
aria-label={i18n.t("number_of_points", {
|
aria-label={i18n.t("number_of_points", {
|
||||||
count: this.state.score,
|
count: this.state.score,
|
||||||
formattedCount: this.state.score,
|
formattedCount: this.state.score,
|
||||||
|
@ -321,6 +326,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
onReplyCancel={this.handleReplyCancel}
|
onReplyCancel={this.handleReplyCancel}
|
||||||
disabled={this.props.locked}
|
disabled={this.props.locked}
|
||||||
focus
|
focus
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!this.state.showEdit && !this.state.collapsed && (
|
{!this.state.showEdit && !this.state.collapsed && (
|
||||||
|
@ -335,11 +341,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div class="d-flex justify-content-between justify-content-lg-start flex-wrap text-muted font-weight-bold">
|
<div className="d-flex justify-content-between justify-content-lg-start flex-wrap text-muted font-weight-bold">
|
||||||
{this.props.showContext && this.linkBtn()}
|
{this.props.showContext && this.linkBtn()}
|
||||||
{this.props.markable && (
|
{this.props.markable && (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(this, this.handleMarkRead)}
|
onClick={linkEvent(this, this.handleMarkRead)}
|
||||||
data-tippy-content={
|
data-tippy-content={
|
||||||
this.commentReplyOrMentionRead
|
this.commentReplyOrMentionRead
|
||||||
|
@ -380,7 +386,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
<Icon icon="arrow-up1" classes="icon-inline" />
|
<Icon icon="arrow-up1" classes="icon-inline" />
|
||||||
{showScores() &&
|
{showScores() &&
|
||||||
this.state.upvotes !== this.state.score && (
|
this.state.upvotes !== this.state.score && (
|
||||||
<span class="ml-1">
|
<span className="ml-1">
|
||||||
{numToSI(this.state.upvotes)}
|
{numToSI(this.state.upvotes)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
@ -399,14 +405,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
<Icon icon="arrow-down1" classes="icon-inline" />
|
<Icon icon="arrow-down1" classes="icon-inline" />
|
||||||
{showScores() &&
|
{showScores() &&
|
||||||
this.state.upvotes !== this.state.score && (
|
this.state.upvotes !== this.state.score && (
|
||||||
<span class="ml-1">
|
<span className="ml-1">
|
||||||
{numToSI(this.state.downvotes)}
|
{numToSI(this.state.downvotes)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(this, this.handleReplyClick)}
|
onClick={linkEvent(this, this.handleReplyClick)}
|
||||||
data-tippy-content={i18n.t("reply")}
|
data-tippy-content={i18n.t("reply")}
|
||||||
aria-label={i18n.t("reply")}
|
aria-label={i18n.t("reply")}
|
||||||
|
@ -426,7 +432,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
<>
|
<>
|
||||||
{!this.myComment && (
|
{!this.myComment && (
|
||||||
<>
|
<>
|
||||||
<button class="btn btn-link btn-animate">
|
<button className="btn btn-link btn-animate">
|
||||||
<Link
|
<Link
|
||||||
className="text-muted"
|
className="text-muted"
|
||||||
to={`/create_private_message/recipient/${cv.creator.id}`}
|
to={`/create_private_message/recipient/${cv.creator.id}`}
|
||||||
|
@ -436,7 +442,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
</Link>
|
</Link>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleShowReportDialog
|
this.handleShowReportDialog
|
||||||
|
@ -449,7 +455,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
<Icon icon="flag" />
|
<Icon icon="flag" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleBlockUserClick
|
this.handleBlockUserClick
|
||||||
|
@ -462,7 +468,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleSaveCommentClick
|
this.handleSaveCommentClick
|
||||||
|
@ -501,7 +507,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
{this.myComment && (
|
{this.myComment && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleEditClick
|
this.handleEditClick
|
||||||
|
@ -512,7 +518,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
<Icon icon="edit" classes="icon-inline" />
|
<Icon icon="edit" classes="icon-inline" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleDeleteClick
|
this.handleDeleteClick
|
||||||
|
@ -538,7 +544,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
|
|
||||||
{(canModOnSelf || canAdminOnSelf) && (
|
{(canModOnSelf || canAdminOnSelf) && (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleDistinguishClick
|
this.handleDistinguishClick
|
||||||
|
@ -570,7 +576,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
<>
|
<>
|
||||||
{!cv.comment.removed ? (
|
{!cv.comment.removed ? (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleModRemoveShow
|
this.handleModRemoveShow
|
||||||
|
@ -581,7 +587,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleModRemoveSubmit
|
this.handleModRemoveSubmit
|
||||||
|
@ -599,7 +605,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
{!isMod_ &&
|
{!isMod_ &&
|
||||||
(!cv.creator_banned_from_community ? (
|
(!cv.creator_banned_from_community ? (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleModBanFromCommunityShow
|
this.handleModBanFromCommunityShow
|
||||||
|
@ -610,7 +616,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleModBanFromCommunitySubmit
|
this.handleModBanFromCommunitySubmit
|
||||||
|
@ -623,7 +629,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
{!cv.creator_banned_from_community &&
|
{!cv.creator_banned_from_community &&
|
||||||
(!this.state.showConfirmAppointAsMod ? (
|
(!this.state.showConfirmAppointAsMod ? (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleShowConfirmAppointAsMod
|
this.handleShowConfirmAppointAsMod
|
||||||
|
@ -641,13 +647,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
aria-label={i18n.t("are_you_sure")}
|
aria-label={i18n.t("are_you_sure")}
|
||||||
>
|
>
|
||||||
{i18n.t("are_you_sure")}
|
{i18n.t("are_you_sure")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleAddModToCommunity
|
this.handleAddModToCommunity
|
||||||
|
@ -657,7 +663,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
{i18n.t("yes")}
|
{i18n.t("yes")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleCancelConfirmAppointAsMod
|
this.handleCancelConfirmAppointAsMod
|
||||||
|
@ -676,7 +682,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
cv.creator.local &&
|
cv.creator.local &&
|
||||||
(!this.state.showConfirmTransferCommunity ? (
|
(!this.state.showConfirmTransferCommunity ? (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleShowConfirmTransferCommunity
|
this.handleShowConfirmTransferCommunity
|
||||||
|
@ -688,13 +694,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
aria-label={i18n.t("are_you_sure")}
|
aria-label={i18n.t("are_you_sure")}
|
||||||
>
|
>
|
||||||
{i18n.t("are_you_sure")}
|
{i18n.t("are_you_sure")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleTransferCommunity
|
this.handleTransferCommunity
|
||||||
|
@ -704,7 +710,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
{i18n.t("yes")}
|
{i18n.t("yes")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this
|
this
|
||||||
|
@ -722,7 +728,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
{!isAdmin_ && (
|
{!isAdmin_ && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handlePurgePersonShow
|
this.handlePurgePersonShow
|
||||||
|
@ -732,7 +738,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
{i18n.t("purge_user")}
|
{i18n.t("purge_user")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handlePurgeCommentShow
|
this.handlePurgeCommentShow
|
||||||
|
@ -744,7 +750,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
|
|
||||||
{!isBanned(cv.creator) ? (
|
{!isBanned(cv.creator) ? (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleModBanShow
|
this.handleModBanShow
|
||||||
|
@ -755,7 +761,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleModBanSubmit
|
this.handleModBanSubmit
|
||||||
|
@ -771,7 +777,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
cv.creator.local &&
|
cv.creator.local &&
|
||||||
(!this.state.showConfirmAppointAsAdmin ? (
|
(!this.state.showConfirmAppointAsAdmin ? (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleShowConfirmAppointAsAdmin
|
this.handleShowConfirmAppointAsAdmin
|
||||||
|
@ -788,11 +794,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<button class="btn btn-link btn-animate text-muted">
|
<button className="btn btn-link btn-animate text-muted">
|
||||||
{i18n.t("are_you_sure")}
|
{i18n.t("are_you_sure")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleAddAdmin
|
this.handleAddAdmin
|
||||||
|
@ -802,7 +808,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
{i18n.t("yes")}
|
{i18n.t("yes")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleCancelConfirmAppointAsAdmin
|
this.handleCancelConfirmAppointAsAdmin
|
||||||
|
@ -833,7 +839,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
style={`border-left: 2px ${moreRepliesBorderColor} solid !important`}
|
style={`border-left: 2px ${moreRepliesBorderColor} solid !important`}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link text-muted"
|
className="btn btn-link text-muted"
|
||||||
onClick={linkEvent(this, this.handleFetchChildren)}
|
onClick={linkEvent(this, this.handleFetchChildren)}
|
||||||
>
|
>
|
||||||
{i18n.t("x_more_replies", {
|
{i18n.t("x_more_replies", {
|
||||||
|
@ -847,11 +853,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
{/* end of details */}
|
{/* end of details */}
|
||||||
{this.state.showRemoveDialog && (
|
{this.state.showRemoveDialog && (
|
||||||
<form
|
<form
|
||||||
class="form-inline"
|
className="form-inline"
|
||||||
onSubmit={linkEvent(this, this.handleModRemoveSubmit)}
|
onSubmit={linkEvent(this, this.handleModRemoveSubmit)}
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
class="sr-only"
|
className="sr-only"
|
||||||
htmlFor={`mod-remove-reason-${cv.comment.id}`}
|
htmlFor={`mod-remove-reason-${cv.comment.id}`}
|
||||||
>
|
>
|
||||||
{i18n.t("reason")}
|
{i18n.t("reason")}
|
||||||
|
@ -859,14 +865,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id={`mod-remove-reason-${cv.comment.id}`}
|
id={`mod-remove-reason-${cv.comment.id}`}
|
||||||
class="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("reason")}
|
placeholder={i18n.t("reason")}
|
||||||
value={toUndefined(this.state.removeReason)}
|
value={toUndefined(this.state.removeReason)}
|
||||||
onInput={linkEvent(this, this.handleModRemoveReasonChange)}
|
onInput={linkEvent(this, this.handleModRemoveReasonChange)}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
aria-label={i18n.t("remove_comment")}
|
aria-label={i18n.t("remove_comment")}
|
||||||
>
|
>
|
||||||
{i18n.t("remove_comment")}
|
{i18n.t("remove_comment")}
|
||||||
|
@ -875,24 +881,27 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
)}
|
)}
|
||||||
{this.state.showReportDialog && (
|
{this.state.showReportDialog && (
|
||||||
<form
|
<form
|
||||||
class="form-inline"
|
className="form-inline"
|
||||||
onSubmit={linkEvent(this, this.handleReportSubmit)}
|
onSubmit={linkEvent(this, this.handleReportSubmit)}
|
||||||
>
|
>
|
||||||
<label class="sr-only" htmlFor={`report-reason-${cv.comment.id}`}>
|
<label
|
||||||
|
className="sr-only"
|
||||||
|
htmlFor={`report-reason-${cv.comment.id}`}
|
||||||
|
>
|
||||||
{i18n.t("reason")}
|
{i18n.t("reason")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
id={`report-reason-${cv.comment.id}`}
|
id={`report-reason-${cv.comment.id}`}
|
||||||
class="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("reason")}
|
placeholder={i18n.t("reason")}
|
||||||
value={this.state.reportReason}
|
value={this.state.reportReason}
|
||||||
onInput={linkEvent(this, this.handleReportReasonChange)}
|
onInput={linkEvent(this, this.handleReportReasonChange)}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
aria-label={i18n.t("create_report")}
|
aria-label={i18n.t("create_report")}
|
||||||
>
|
>
|
||||||
{i18n.t("create_report")}
|
{i18n.t("create_report")}
|
||||||
|
@ -901,9 +910,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
)}
|
)}
|
||||||
{this.state.showBanDialog && (
|
{this.state.showBanDialog && (
|
||||||
<form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}>
|
||||||
<div class="form-group row col-12">
|
<div className="form-group row col-12">
|
||||||
<label
|
<label
|
||||||
class="col-form-label"
|
className="col-form-label"
|
||||||
htmlFor={`mod-ban-reason-${cv.comment.id}`}
|
htmlFor={`mod-ban-reason-${cv.comment.id}`}
|
||||||
>
|
>
|
||||||
{i18n.t("reason")}
|
{i18n.t("reason")}
|
||||||
|
@ -911,13 +920,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id={`mod-ban-reason-${cv.comment.id}`}
|
id={`mod-ban-reason-${cv.comment.id}`}
|
||||||
class="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("reason")}
|
placeholder={i18n.t("reason")}
|
||||||
value={toUndefined(this.state.banReason)}
|
value={toUndefined(this.state.banReason)}
|
||||||
onInput={linkEvent(this, this.handleModBanReasonChange)}
|
onInput={linkEvent(this, this.handleModBanReasonChange)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
class="col-form-label"
|
className="col-form-label"
|
||||||
htmlFor={`mod-ban-expires-${cv.comment.id}`}
|
htmlFor={`mod-ban-expires-${cv.comment.id}`}
|
||||||
>
|
>
|
||||||
{i18n.t("expires")}
|
{i18n.t("expires")}
|
||||||
|
@ -925,22 +934,22 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
id={`mod-ban-expires-${cv.comment.id}`}
|
id={`mod-ban-expires-${cv.comment.id}`}
|
||||||
class="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("number_of_days")}
|
placeholder={i18n.t("number_of_days")}
|
||||||
value={toUndefined(this.state.banExpireDays)}
|
value={toUndefined(this.state.banExpireDays)}
|
||||||
onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
|
onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
|
||||||
/>
|
/>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="mod-ban-remove-data"
|
id="mod-ban-remove-data"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.removeData}
|
checked={this.state.removeData}
|
||||||
onChange={linkEvent(this, this.handleModRemoveDataChange)}
|
onChange={linkEvent(this, this.handleModRemoveDataChange)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
class="form-check-label"
|
className="form-check-label"
|
||||||
htmlFor="mod-ban-remove-data"
|
htmlFor="mod-ban-remove-data"
|
||||||
title={i18n.t("remove_content_more")}
|
title={i18n.t("remove_content_more")}
|
||||||
>
|
>
|
||||||
|
@ -954,10 +963,10 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
{/* <label class="col-form-label">Expires</label> */}
|
{/* <label class="col-form-label">Expires</label> */}
|
||||||
{/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
|
{/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
|
||||||
{/* </div> */}
|
{/* </div> */}
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
aria-label={i18n.t("ban")}
|
aria-label={i18n.t("ban")}
|
||||||
>
|
>
|
||||||
{i18n.t("ban")} {cv.creator.name}
|
{i18n.t("ban")} {cv.creator.name}
|
||||||
|
@ -969,24 +978,24 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
{this.state.showPurgeDialog && (
|
{this.state.showPurgeDialog && (
|
||||||
<form onSubmit={linkEvent(this, this.handlePurgeSubmit)}>
|
<form onSubmit={linkEvent(this, this.handlePurgeSubmit)}>
|
||||||
<PurgeWarning />
|
<PurgeWarning />
|
||||||
<label class="sr-only" htmlFor="purge-reason">
|
<label className="sr-only" htmlFor="purge-reason">
|
||||||
{i18n.t("reason")}
|
{i18n.t("reason")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="purge-reason"
|
id="purge-reason"
|
||||||
class="form-control my-3"
|
className="form-control my-3"
|
||||||
placeholder={i18n.t("reason")}
|
placeholder={i18n.t("reason")}
|
||||||
value={toUndefined(this.state.purgeReason)}
|
value={toUndefined(this.state.purgeReason)}
|
||||||
onInput={linkEvent(this, this.handlePurgeReasonChange)}
|
onInput={linkEvent(this, this.handlePurgeReasonChange)}
|
||||||
/>
|
/>
|
||||||
<div class="form-group row col-12">
|
<div className="form-group row col-12">
|
||||||
{this.state.purgeLoading ? (
|
{this.state.purgeLoading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
aria-label={purgeTypeText}
|
aria-label={purgeTypeText}
|
||||||
>
|
>
|
||||||
{purgeTypeText}
|
{purgeTypeText}
|
||||||
|
@ -1001,6 +1010,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
onReplyCancel={this.handleReplyCancel}
|
onReplyCancel={this.handleReplyCancel}
|
||||||
disabled={this.props.locked}
|
disabled={this.props.locked}
|
||||||
focus
|
focus
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!this.state.collapsed && node.children.length > 0 && (
|
{!this.state.collapsed && node.children.length > 0 && (
|
||||||
|
@ -1012,10 +1022,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
maxCommentsShown={None}
|
maxCommentsShown={None}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
viewType={this.props.viewType}
|
viewType={this.props.viewType}
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* A collapsed clearfix */}
|
{/* A collapsed clearfix */}
|
||||||
{this.state.collapsed && <div class="row col-12"></div>}
|
{this.state.collapsed && <div className="row col-12"></div>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1090,13 +1101,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReplyClick(i: CommentNode) {
|
handleReplyClick(i: CommentNode) {
|
||||||
i.state.showReply = true;
|
i.setState({ showReply: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditClick(i: CommentNode) {
|
handleEditClick(i: CommentNode) {
|
||||||
i.state.showEdit = true;
|
i.setState({ showEdit: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBlockUserClick(i: CommentNode) {
|
handleBlockUserClick(i: CommentNode) {
|
||||||
|
@ -1129,14 +1138,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
|
|
||||||
WebSocketService.Instance.send(wsClient.saveComment(form));
|
WebSocketService.Instance.send(wsClient.saveComment(form));
|
||||||
|
|
||||||
i.state.saveLoading = true;
|
i.setState({ saveLoading: true });
|
||||||
i.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReplyCancel() {
|
handleReplyCancel() {
|
||||||
this.state.showReply = false;
|
this.setState({ showReply: false, showEdit: false });
|
||||||
this.state.showEdit = false;
|
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommentUpvote(event: any) {
|
handleCommentUpvote(event: any) {
|
||||||
|
@ -1145,18 +1151,24 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
let newVote = myVote == 1 ? 0 : 1;
|
let newVote = myVote == 1 ? 0 : 1;
|
||||||
|
|
||||||
if (myVote == 1) {
|
if (myVote == 1) {
|
||||||
this.state.score--;
|
this.setState({
|
||||||
this.state.upvotes--;
|
score: this.state.score - 1,
|
||||||
|
upvotes: this.state.upvotes - 1,
|
||||||
|
});
|
||||||
} else if (myVote == -1) {
|
} else if (myVote == -1) {
|
||||||
this.state.downvotes--;
|
this.setState({
|
||||||
this.state.upvotes++;
|
downvotes: this.state.downvotes - 1,
|
||||||
this.state.score += 2;
|
upvotes: this.state.upvotes + 1,
|
||||||
|
score: this.state.score + 2,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.state.upvotes++;
|
this.setState({
|
||||||
this.state.score++;
|
score: this.state.score + 1,
|
||||||
|
upvotes: this.state.upvotes + 1,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state.my_vote = Some(newVote);
|
this.setState({ my_vote: Some(newVote) });
|
||||||
|
|
||||||
let form = new CreateCommentLike({
|
let form = new CreateCommentLike({
|
||||||
comment_id: this.props.node.comment_view.comment.id,
|
comment_id: this.props.node.comment_view.comment.id,
|
||||||
|
@ -1164,7 +1176,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
auth: auth().unwrap(),
|
auth: auth().unwrap(),
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.likeComment(form));
|
WebSocketService.Instance.send(wsClient.likeComment(form));
|
||||||
this.setState(this.state);
|
|
||||||
setupTippy();
|
setupTippy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1174,18 +1185,24 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
let newVote = myVote == -1 ? 0 : -1;
|
let newVote = myVote == -1 ? 0 : -1;
|
||||||
|
|
||||||
if (myVote == 1) {
|
if (myVote == 1) {
|
||||||
this.state.score -= 2;
|
this.setState({
|
||||||
this.state.upvotes--;
|
downvotes: this.state.downvotes + 1,
|
||||||
this.state.downvotes++;
|
upvotes: this.state.upvotes - 1,
|
||||||
|
score: this.state.score - 2,
|
||||||
|
});
|
||||||
} else if (myVote == -1) {
|
} else if (myVote == -1) {
|
||||||
this.state.downvotes--;
|
this.setState({
|
||||||
this.state.score++;
|
downvotes: this.state.downvotes - 1,
|
||||||
|
score: this.state.score + 1,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.state.downvotes++;
|
this.setState({
|
||||||
this.state.score--;
|
downvotes: this.state.downvotes + 1,
|
||||||
|
score: this.state.score - 1,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state.my_vote = Some(newVote);
|
this.setState({ my_vote: Some(newVote) });
|
||||||
|
|
||||||
let form = new CreateCommentLike({
|
let form = new CreateCommentLike({
|
||||||
comment_id: this.props.node.comment_view.comment.id,
|
comment_id: this.props.node.comment_view.comment.id,
|
||||||
|
@ -1194,18 +1211,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
});
|
});
|
||||||
|
|
||||||
WebSocketService.Instance.send(wsClient.likeComment(form));
|
WebSocketService.Instance.send(wsClient.likeComment(form));
|
||||||
this.setState(this.state);
|
|
||||||
setupTippy();
|
setupTippy();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowReportDialog(i: CommentNode) {
|
handleShowReportDialog(i: CommentNode) {
|
||||||
i.state.showReportDialog = !i.state.showReportDialog;
|
i.setState({ showReportDialog: !i.state.showReportDialog });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReportReasonChange(i: CommentNode, event: any) {
|
handleReportReasonChange(i: CommentNode, event: any) {
|
||||||
i.state.reportReason = event.target.value;
|
i.setState({ reportReason: event.target.value });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReportSubmit(i: CommentNode) {
|
handleReportSubmit(i: CommentNode) {
|
||||||
|
@ -1217,24 +1231,22 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.createCommentReport(form));
|
WebSocketService.Instance.send(wsClient.createCommentReport(form));
|
||||||
|
|
||||||
i.state.showReportDialog = false;
|
i.setState({ showReportDialog: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveShow(i: CommentNode) {
|
handleModRemoveShow(i: CommentNode) {
|
||||||
i.state.showRemoveDialog = !i.state.showRemoveDialog;
|
i.setState({
|
||||||
i.state.showBanDialog = false;
|
showRemoveDialog: !i.state.showRemoveDialog,
|
||||||
i.setState(i.state);
|
showBanDialog: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveReasonChange(i: CommentNode, event: any) {
|
handleModRemoveReasonChange(i: CommentNode, event: any) {
|
||||||
i.state.removeReason = Some(event.target.value);
|
i.setState({ removeReason: Some(event.target.value) });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveDataChange(i: CommentNode, event: any) {
|
handleModRemoveDataChange(i: CommentNode, event: any) {
|
||||||
i.state.removeData = event.target.checked;
|
i.setState({ removeData: event.target.checked });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveSubmit(i: CommentNode) {
|
handleModRemoveSubmit(i: CommentNode) {
|
||||||
|
@ -1247,8 +1259,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.removeComment(form));
|
WebSocketService.Instance.send(wsClient.removeComment(form));
|
||||||
|
|
||||||
i.state.showRemoveDialog = false;
|
i.setState({ showRemoveDialog: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDistinguishClick(i: CommentNode) {
|
handleDistinguishClick(i: CommentNode) {
|
||||||
|
@ -1258,6 +1269,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
form_id: None, // TODO not sure about this
|
form_id: None, // TODO not sure about this
|
||||||
content: None,
|
content: None,
|
||||||
distinguished: Some(!comment.distinguished),
|
distinguished: Some(!comment.distinguished),
|
||||||
|
language_id: Some(comment.language_id),
|
||||||
auth: auth().unwrap(),
|
auth: auth().unwrap(),
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.editComment(form));
|
WebSocketService.Instance.send(wsClient.editComment(form));
|
||||||
|
@ -1293,43 +1305,40 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
WebSocketService.Instance.send(wsClient.markCommentReplyAsRead(form));
|
WebSocketService.Instance.send(wsClient.markCommentReplyAsRead(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
i.state.readLoading = true;
|
i.setState({ readLoading: true });
|
||||||
i.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanFromCommunityShow(i: CommentNode) {
|
handleModBanFromCommunityShow(i: CommentNode) {
|
||||||
i.state.showBanDialog = true;
|
i.setState({
|
||||||
i.state.banType = BanType.Community;
|
showBanDialog: true,
|
||||||
i.state.showRemoveDialog = false;
|
banType: BanType.Community,
|
||||||
i.setState(i.state);
|
showRemoveDialog: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanShow(i: CommentNode) {
|
handleModBanShow(i: CommentNode) {
|
||||||
i.state.showBanDialog = true;
|
i.setState({
|
||||||
i.state.banType = BanType.Site;
|
showBanDialog: true,
|
||||||
i.state.showRemoveDialog = false;
|
banType: BanType.Site,
|
||||||
i.setState(i.state);
|
showRemoveDialog: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanReasonChange(i: CommentNode, event: any) {
|
handleModBanReasonChange(i: CommentNode, event: any) {
|
||||||
i.state.banReason = Some(event.target.value);
|
i.setState({ banReason: Some(event.target.value) });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanExpireDaysChange(i: CommentNode, event: any) {
|
handleModBanExpireDaysChange(i: CommentNode, event: any) {
|
||||||
i.state.banExpireDays = Some(event.target.value);
|
i.setState({ banExpireDays: Some(event.target.value) });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanFromCommunitySubmit(i: CommentNode) {
|
handleModBanFromCommunitySubmit(i: CommentNode) {
|
||||||
i.state.banType = BanType.Community;
|
i.setState({ banType: BanType.Community });
|
||||||
i.setState(i.state);
|
|
||||||
i.handleModBanBothSubmit(i);
|
i.handleModBanBothSubmit(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanSubmit(i: CommentNode) {
|
handleModBanSubmit(i: CommentNode) {
|
||||||
i.state.banType = BanType.Site;
|
i.setState({ banType: BanType.Site });
|
||||||
i.setState(i.state);
|
|
||||||
i.handleModBanBothSubmit(i);
|
i.handleModBanBothSubmit(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1340,7 +1349,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
// If its an unban, restore all their data
|
// If its an unban, restore all their data
|
||||||
let ban = !cv.creator_banned_from_community;
|
let ban = !cv.creator_banned_from_community;
|
||||||
if (ban == false) {
|
if (ban == false) {
|
||||||
i.state.removeData = false;
|
i.setState({ removeData: false });
|
||||||
}
|
}
|
||||||
let form = new BanFromCommunity({
|
let form = new BanFromCommunity({
|
||||||
person_id: cv.creator.id,
|
person_id: cv.creator.id,
|
||||||
|
@ -1356,7 +1365,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
// If its an unban, restore all their data
|
// If its an unban, restore all their data
|
||||||
let ban = !cv.creator.banned;
|
let ban = !cv.creator.banned;
|
||||||
if (ban == false) {
|
if (ban == false) {
|
||||||
i.state.removeData = false;
|
i.setState({ removeData: false });
|
||||||
}
|
}
|
||||||
let form = new BanPerson({
|
let form = new BanPerson({
|
||||||
person_id: cv.creator.id,
|
person_id: cv.creator.id,
|
||||||
|
@ -1369,27 +1378,27 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
WebSocketService.Instance.send(wsClient.banPerson(form));
|
WebSocketService.Instance.send(wsClient.banPerson(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
i.state.showBanDialog = false;
|
i.setState({ showBanDialog: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePurgePersonShow(i: CommentNode) {
|
handlePurgePersonShow(i: CommentNode) {
|
||||||
i.state.showPurgeDialog = true;
|
i.setState({
|
||||||
i.state.purgeType = PurgeType.Person;
|
showPurgeDialog: true,
|
||||||
i.state.showRemoveDialog = false;
|
purgeType: PurgeType.Person,
|
||||||
i.setState(i.state);
|
showRemoveDialog: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePurgeCommentShow(i: CommentNode) {
|
handlePurgeCommentShow(i: CommentNode) {
|
||||||
i.state.showPurgeDialog = true;
|
i.setState({
|
||||||
i.state.purgeType = PurgeType.Comment;
|
showPurgeDialog: true,
|
||||||
i.state.showRemoveDialog = false;
|
purgeType: PurgeType.Comment,
|
||||||
i.setState(i.state);
|
showRemoveDialog: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePurgeReasonChange(i: CommentNode, event: any) {
|
handlePurgeReasonChange(i: CommentNode, event: any) {
|
||||||
i.state.purgeReason = Some(event.target.value);
|
i.setState({ purgeReason: Some(event.target.value) });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePurgeSubmit(i: CommentNode, event: any) {
|
handlePurgeSubmit(i: CommentNode, event: any) {
|
||||||
|
@ -1411,18 +1420,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
WebSocketService.Instance.send(wsClient.purgeComment(form));
|
WebSocketService.Instance.send(wsClient.purgeComment(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
i.state.purgeLoading = true;
|
i.setState({ purgeLoading: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowConfirmAppointAsMod(i: CommentNode) {
|
handleShowConfirmAppointAsMod(i: CommentNode) {
|
||||||
i.state.showConfirmAppointAsMod = true;
|
i.setState({ showConfirmAppointAsMod: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancelConfirmAppointAsMod(i: CommentNode) {
|
handleCancelConfirmAppointAsMod(i: CommentNode) {
|
||||||
i.state.showConfirmAppointAsMod = false;
|
i.setState({ showConfirmAppointAsMod: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAddModToCommunity(i: CommentNode) {
|
handleAddModToCommunity(i: CommentNode) {
|
||||||
|
@ -1434,18 +1440,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
auth: auth().unwrap(),
|
auth: auth().unwrap(),
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.addModToCommunity(form));
|
WebSocketService.Instance.send(wsClient.addModToCommunity(form));
|
||||||
i.state.showConfirmAppointAsMod = false;
|
i.setState({ showConfirmAppointAsMod: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowConfirmAppointAsAdmin(i: CommentNode) {
|
handleShowConfirmAppointAsAdmin(i: CommentNode) {
|
||||||
i.state.showConfirmAppointAsAdmin = true;
|
i.setState({ showConfirmAppointAsAdmin: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancelConfirmAppointAsAdmin(i: CommentNode) {
|
handleCancelConfirmAppointAsAdmin(i: CommentNode) {
|
||||||
i.state.showConfirmAppointAsAdmin = false;
|
i.setState({ showConfirmAppointAsAdmin: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAddAdmin(i: CommentNode) {
|
handleAddAdmin(i: CommentNode) {
|
||||||
|
@ -1456,18 +1459,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
auth: auth().unwrap(),
|
auth: auth().unwrap(),
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.addAdmin(form));
|
WebSocketService.Instance.send(wsClient.addAdmin(form));
|
||||||
i.state.showConfirmAppointAsAdmin = false;
|
i.setState({ showConfirmAppointAsAdmin: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowConfirmTransferCommunity(i: CommentNode) {
|
handleShowConfirmTransferCommunity(i: CommentNode) {
|
||||||
i.state.showConfirmTransferCommunity = true;
|
i.setState({ showConfirmTransferCommunity: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancelShowConfirmTransferCommunity(i: CommentNode) {
|
handleCancelShowConfirmTransferCommunity(i: CommentNode) {
|
||||||
i.state.showConfirmTransferCommunity = false;
|
i.setState({ showConfirmTransferCommunity: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTransferCommunity(i: CommentNode) {
|
handleTransferCommunity(i: CommentNode) {
|
||||||
|
@ -1478,18 +1478,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
auth: auth().unwrap(),
|
auth: auth().unwrap(),
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.transferCommunity(form));
|
WebSocketService.Instance.send(wsClient.transferCommunity(form));
|
||||||
i.state.showConfirmTransferCommunity = false;
|
i.setState({ showConfirmTransferCommunity: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowConfirmTransferSite(i: CommentNode) {
|
handleShowConfirmTransferSite(i: CommentNode) {
|
||||||
i.state.showConfirmTransferSite = true;
|
i.setState({ showConfirmTransferSite: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancelShowConfirmTransferSite(i: CommentNode) {
|
handleCancelShowConfirmTransferSite(i: CommentNode) {
|
||||||
i.state.showConfirmTransferSite = false;
|
i.setState({ showConfirmTransferSite: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isCommentNew(): boolean {
|
get isCommentNew(): boolean {
|
||||||
|
@ -1499,19 +1496,16 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommentCollapse(i: CommentNode) {
|
handleCommentCollapse(i: CommentNode) {
|
||||||
i.state.collapsed = !i.state.collapsed;
|
i.setState({ collapsed: !i.state.collapsed });
|
||||||
i.setState(i.state);
|
|
||||||
setupTippy();
|
setupTippy();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleViewSource(i: CommentNode) {
|
handleViewSource(i: CommentNode) {
|
||||||
i.state.viewSource = !i.state.viewSource;
|
i.setState({ viewSource: !i.state.viewSource });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowAdvanced(i: CommentNode) {
|
handleShowAdvanced(i: CommentNode) {
|
||||||
i.state.showAdvanced = !i.state.showAdvanced;
|
i.setState({ showAdvanced: !i.state.showAdvanced });
|
||||||
i.setState(i.state);
|
|
||||||
setupTippy();
|
setupTippy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Component } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommentNode as CommentNodeI,
|
CommentNode as CommentNodeI,
|
||||||
CommunityModeratorView,
|
CommunityModeratorView,
|
||||||
|
Language,
|
||||||
PersonViewSafe,
|
PersonViewSafe,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { CommentViewType } from "../../interfaces";
|
import { CommentViewType } from "../../interfaces";
|
||||||
|
@ -22,6 +23,7 @@ interface CommentNodesProps {
|
||||||
showCommunity?: boolean;
|
showCommunity?: boolean;
|
||||||
enableDownvotes?: boolean;
|
enableDownvotes?: boolean;
|
||||||
viewType: CommentViewType;
|
viewType: CommentViewType;
|
||||||
|
allLanguages: Language[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommentNodes extends Component<CommentNodesProps, any> {
|
export class CommentNodes extends Component<CommentNodesProps, any> {
|
||||||
|
@ -51,6 +53,7 @@ export class CommentNodes extends Component<CommentNodesProps, any> {
|
||||||
showCommunity={this.props.showCommunity}
|
showCommunity={this.props.showCommunity}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
viewType={this.props.viewType}
|
viewType={this.props.viewType}
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -64,6 +64,7 @@ export class CommentReport extends Component<CommentReportProps, any> {
|
||||||
enableDownvotes={true}
|
enableDownvotes={true}
|
||||||
viewOnly={true}
|
viewOnly={true}
|
||||||
showCommunity={true}
|
showCommunity={true}
|
||||||
|
allLanguages={[]}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
{i18n.t("reporter")}: <PersonListing person={r.creator} />
|
{i18n.t("reporter")}: <PersonListing person={r.creator} />
|
||||||
|
|
|
@ -14,7 +14,7 @@ export class BannerIconHeader extends Component<BannerIconHeaderProps, any> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="position-relative mb-2">
|
<div className="position-relative mb-2">
|
||||||
{this.props.banner.match({
|
{this.props.banner.match({
|
||||||
some: banner => <PictrsImage src={banner} banner alt="" />,
|
some: banner => <PictrsImage src={banner} banner alt="" />,
|
||||||
none: <></>,
|
none: <></>,
|
||||||
|
|
|
@ -41,7 +41,7 @@ export class CommentSortSelect extends Component<
|
||||||
name={this.id}
|
name={this.id}
|
||||||
value={this.state.sort}
|
value={this.state.sort}
|
||||||
onChange={linkEvent(this, this.handleSortChange)}
|
onChange={linkEvent(this, this.handleSortChange)}
|
||||||
class="custom-select w-auto mr-2 mb-2"
|
className="custom-select w-auto mr-2 mb-2"
|
||||||
aria-label={i18n.t("sort_type")}
|
aria-label={i18n.t("sort_type")}
|
||||||
>
|
>
|
||||||
<option disabled aria-hidden="true">
|
<option disabled aria-hidden="true">
|
||||||
|
|
|
@ -32,7 +32,7 @@ export class DataTypeSelect extends Component<
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="btn-group btn-group-toggle flex-wrap mb-2">
|
<div className="btn-group btn-group-toggle flex-wrap mb-2">
|
||||||
<label
|
<label
|
||||||
className={`pointer btn btn-outline-secondary
|
className={`pointer btn btn-outline-secondary
|
||||||
${this.state.type_ == DataType.Post && "active"}
|
${this.state.type_ == DataType.Post && "active"}
|
||||||
|
|
|
@ -19,10 +19,10 @@ export class HtmlTags extends Component<HtmlTagsProps, any> {
|
||||||
return (
|
return (
|
||||||
<Helmet title={this.props.title}>
|
<Helmet title={this.props.title}>
|
||||||
{["title", "og:title", "twitter:title"].map(t => (
|
{["title", "og:title", "twitter:title"].map(t => (
|
||||||
<meta property={t} content={this.props.title} />
|
<meta key={t} property={t} content={this.props.title} />
|
||||||
))}
|
))}
|
||||||
{["og:url", "twitter:url"].map(u => (
|
{["og:url", "twitter:url"].map(u => (
|
||||||
<meta property={u} content={url} />
|
<meta key={u} property={u} content={url} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Open Graph / Facebook */}
|
{/* Open Graph / Facebook */}
|
||||||
|
@ -35,6 +35,7 @@ export class HtmlTags extends Component<HtmlTagsProps, any> {
|
||||||
{this.props.description.isSome() &&
|
{this.props.description.isSome() &&
|
||||||
["description", "og:description", "twitter:description"].map(n => (
|
["description", "og:description", "twitter:description"].map(n => (
|
||||||
<meta
|
<meta
|
||||||
|
key={n}
|
||||||
name={n}
|
name={n}
|
||||||
content={md.renderInline(this.props.description.unwrap())}
|
content={md.renderInline(this.props.description.unwrap())}
|
||||||
/>
|
/>
|
||||||
|
@ -42,7 +43,7 @@ export class HtmlTags extends Component<HtmlTagsProps, any> {
|
||||||
|
|
||||||
{this.props.image.isSome() &&
|
{this.props.image.isSome() &&
|
||||||
["og:image", "twitter:image"].map(p => (
|
["og:image", "twitter:image"].map(p => (
|
||||||
<meta property={p} content={this.props.image.unwrap()} />
|
<meta key={p} property={p} content={this.props.image.unwrap()} />
|
||||||
))}
|
))}
|
||||||
</Helmet>
|
</Helmet>
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,13 +17,13 @@ export class Icon extends Component<IconProps, any> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
class={classNames("icon", this.props.classes, {
|
className={classNames("icon", this.props.classes, {
|
||||||
"icon-inline": this.props.inline,
|
"icon-inline": this.props.inline,
|
||||||
small: this.props.small,
|
small: this.props.small,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<use xlinkHref={`#icon-${this.props.icon}`}></use>
|
<use xlinkHref={`#icon-${this.props.icon}`}></use>
|
||||||
<div class="sr-only">
|
<div className="sr-only">
|
||||||
<title>{this.props.icon}</title>
|
<title>{this.props.icon}</title>
|
||||||
</div>
|
</div>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -57,7 +57,7 @@ export class PurgeWarning extends Component<any, any> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="mt-2 alert alert-danger" role="alert">
|
<div className="mt-2 alert alert-danger" role="alert">
|
||||||
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
||||||
{i18n.t("purge_warning")}
|
{i18n.t("purge_warning")}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -34,14 +34,14 @@ export class ImageUploadForm extends Component<
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<form class="d-inline">
|
<form className="d-inline">
|
||||||
<label
|
<label
|
||||||
htmlFor={this.id}
|
htmlFor={this.id}
|
||||||
class="pointer text-muted small font-weight-bold"
|
className="pointer text-muted small font-weight-bold"
|
||||||
>
|
>
|
||||||
{this.props.imageSrc.match({
|
{this.props.imageSrc.match({
|
||||||
some: imageSrc => (
|
some: imageSrc => (
|
||||||
<span class="d-inline-block position-relative">
|
<span className="d-inline-block position-relative">
|
||||||
<img
|
<img
|
||||||
src={imageSrc}
|
src={imageSrc}
|
||||||
height={this.props.rounded ? 60 : ""}
|
height={this.props.rounded ? 60 : ""}
|
||||||
|
@ -59,7 +59,9 @@ export class ImageUploadForm extends Component<
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
none: (
|
none: (
|
||||||
<span class="btn btn-secondary">{this.props.uploadTitle}</span>
|
<span className="btn btn-secondary">
|
||||||
|
{this.props.uploadTitle}
|
||||||
|
</span>
|
||||||
),
|
),
|
||||||
})}
|
})}
|
||||||
</label>
|
</label>
|
||||||
|
@ -68,7 +70,7 @@ export class ImageUploadForm extends Component<
|
||||||
type="file"
|
type="file"
|
||||||
accept="image/*,video/*"
|
accept="image/*,video/*"
|
||||||
name={this.id}
|
name={this.id}
|
||||||
class="d-none"
|
className="d-none"
|
||||||
disabled={UserService.Instance.myUserInfo.isNone()}
|
disabled={UserService.Instance.myUserInfo.isNone()}
|
||||||
onChange={linkEvent(this, this.handleImageUpload)}
|
onChange={linkEvent(this, this.handleImageUpload)}
|
||||||
/>
|
/>
|
||||||
|
@ -82,8 +84,7 @@ export class ImageUploadForm extends Component<
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("images[]", file);
|
formData.append("images[]", file);
|
||||||
|
|
||||||
i.state.loading = true;
|
i.setState({ loading: true });
|
||||||
i.setState(i.state);
|
|
||||||
|
|
||||||
fetch(pictrsUri, {
|
fetch(pictrsUri, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -96,18 +97,15 @@ export class ImageUploadForm extends Component<
|
||||||
if (res.msg == "ok") {
|
if (res.msg == "ok") {
|
||||||
let hash = res.files[0].file;
|
let hash = res.files[0].file;
|
||||||
let url = `${pictrsUri}/${hash}`;
|
let url = `${pictrsUri}/${hash}`;
|
||||||
i.state.loading = false;
|
i.setState({ loading: false });
|
||||||
i.setState(i.state);
|
|
||||||
i.props.onUpload(url);
|
i.props.onUpload(url);
|
||||||
} else {
|
} else {
|
||||||
i.state.loading = false;
|
i.setState({ loading: false });
|
||||||
i.setState(i.state);
|
|
||||||
toast(JSON.stringify(res), "danger");
|
toast(JSON.stringify(res), "danger");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
i.state.loading = false;
|
i.setState({ loading: false });
|
||||||
i.setState(i.state);
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
toast(error, "danger");
|
toast(error, "danger");
|
||||||
});
|
});
|
||||||
|
@ -115,8 +113,7 @@ export class ImageUploadForm extends Component<
|
||||||
|
|
||||||
handleRemoveImage(i: ImageUploadForm, event: any) {
|
handleRemoveImage(i: ImageUploadForm, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.loading = true;
|
i.setState({ loading: true });
|
||||||
i.setState(i.state);
|
|
||||||
i.props.onRemove();
|
i.props.onRemove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
109
src/shared/components/common/language-select.tsx
Normal file
109
src/shared/components/common/language-select.tsx
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import { Option } from "@sniptt/monads";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import { Component, linkEvent } from "inferno";
|
||||||
|
import { Language } from "lemmy-js-client";
|
||||||
|
import { i18n } from "../../i18next";
|
||||||
|
import { randomStr } from "../../utils";
|
||||||
|
import { Icon } from "./icon";
|
||||||
|
|
||||||
|
interface LanguageSelectProps {
|
||||||
|
allLanguages: Language[];
|
||||||
|
selectedLanguageIds: Option<number[]>;
|
||||||
|
multiple: boolean;
|
||||||
|
onChange(val: number[]): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LanguageSelect extends Component<LanguageSelectProps, any> {
|
||||||
|
private id = `language-select-${randomStr()}`;
|
||||||
|
|
||||||
|
constructor(props: any, context: any) {
|
||||||
|
super(props, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.setSelectedValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Necessary because there is no HTML way to set selected for multiple in value=
|
||||||
|
setSelectedValues() {
|
||||||
|
this.props.selectedLanguageIds.map(toString).match({
|
||||||
|
some: ids => {
|
||||||
|
var select = (document.getElementById(this.id) as HTMLSelectElement)
|
||||||
|
.options;
|
||||||
|
for (let i = 0; i < select.length; i++) {
|
||||||
|
let o = select[i];
|
||||||
|
if (ids.includes(o.value)) {
|
||||||
|
o.selected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let selectedLangs = this.props.selectedLanguageIds;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="form-group row">
|
||||||
|
<label
|
||||||
|
className={classNames("col-form-label", {
|
||||||
|
"col-sm-3": this.props.multiple,
|
||||||
|
"col-sm-2": !this.props.multiple,
|
||||||
|
})}
|
||||||
|
htmlFor={this.id}
|
||||||
|
>
|
||||||
|
{i18n.t(this.props.multiple ? "language_plural" : "language")}
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
className={classNames("input-group", {
|
||||||
|
"col-sm-9": this.props.multiple,
|
||||||
|
"col-sm-10": !this.props.multiple,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
className="form-control custom-select"
|
||||||
|
id={this.id}
|
||||||
|
onChange={linkEvent(this, this.handleLanguageChange)}
|
||||||
|
aria-label="action"
|
||||||
|
multiple={this.props.multiple}
|
||||||
|
>
|
||||||
|
{this.props.allLanguages.map(l => (
|
||||||
|
<option
|
||||||
|
key={l.id}
|
||||||
|
value={l.id}
|
||||||
|
selected={selectedLangs.unwrapOr([]).includes(l.id)}
|
||||||
|
>
|
||||||
|
{l.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
{this.props.multiple && (
|
||||||
|
<div className="input-group-append">
|
||||||
|
<button
|
||||||
|
className="input-group-text"
|
||||||
|
onClick={linkEvent(this, this.handleDeselectAll)}
|
||||||
|
>
|
||||||
|
<Icon icon="x" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLanguageChange(i: LanguageSelect, event: any) {
|
||||||
|
let options: HTMLOptionElement[] = Array.from(event.target.options);
|
||||||
|
let selected: number[] = options
|
||||||
|
.filter(o => o.selected)
|
||||||
|
.map(o => Number(o.value));
|
||||||
|
|
||||||
|
i.props.onChange(selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDeselectAll(i: LanguageSelect, event: any) {
|
||||||
|
event.preventDefault();
|
||||||
|
i.props.onChange([]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,7 +40,7 @@ export class ListingTypeSelect extends Component<
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="btn-group btn-group-toggle flex-wrap mb-2">
|
<div className="btn-group btn-group-toggle flex-wrap mb-2">
|
||||||
{this.props.showSubscribed && (
|
{this.props.showSubscribed && (
|
||||||
<label
|
<label
|
||||||
title={i18n.t("subscribed_description")}
|
title={i18n.t("subscribed_description")}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { None, Option, Some } from "@sniptt/monads";
|
||||||
import autosize from "autosize";
|
import autosize from "autosize";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { Prompt } from "inferno-router";
|
import { Prompt } from "inferno-router";
|
||||||
import { toUndefined } from "lemmy-js-client";
|
import { Language, 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,9 +18,11 @@ import {
|
||||||
toast,
|
toast,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { Icon, Spinner } from "./icon";
|
import { Icon, Spinner } from "./icon";
|
||||||
|
import { LanguageSelect } from "./language-select";
|
||||||
|
|
||||||
interface MarkdownTextAreaProps {
|
interface MarkdownTextAreaProps {
|
||||||
initialContent: Option<string>;
|
initialContent: Option<string>;
|
||||||
|
initialLanguageId: Option<number>;
|
||||||
placeholder: Option<string>;
|
placeholder: Option<string>;
|
||||||
buttonTitle: Option<string>;
|
buttonTitle: Option<string>;
|
||||||
maxLength: Option<number>;
|
maxLength: Option<number>;
|
||||||
|
@ -28,14 +30,21 @@ interface MarkdownTextAreaProps {
|
||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
finished?: boolean;
|
finished?: boolean;
|
||||||
|
showLanguage?: boolean;
|
||||||
hideNavigationWarnings?: boolean;
|
hideNavigationWarnings?: boolean;
|
||||||
onContentChange?(val: string): any;
|
onContentChange?(val: string): any;
|
||||||
onReplyCancel?(): any;
|
onReplyCancel?(): any;
|
||||||
onSubmit?(msg: { val: string; formId: string }): any;
|
onSubmit?(msg: {
|
||||||
|
val: Option<string>;
|
||||||
|
formId: string;
|
||||||
|
languageId: Option<number>;
|
||||||
|
}): any;
|
||||||
|
allLanguages: Language[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MarkdownTextAreaState {
|
interface MarkdownTextAreaState {
|
||||||
content: Option<string>;
|
content: Option<string>;
|
||||||
|
languageId: Option<number>;
|
||||||
previewMode: boolean;
|
previewMode: boolean;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
imageLoading: boolean;
|
imageLoading: boolean;
|
||||||
|
@ -50,6 +59,7 @@ export class MarkdownTextArea extends Component<
|
||||||
private tribute: any;
|
private tribute: any;
|
||||||
private emptyState: MarkdownTextAreaState = {
|
private emptyState: MarkdownTextAreaState = {
|
||||||
content: this.props.initialContent,
|
content: this.props.initialContent,
|
||||||
|
languageId: this.props.initialLanguageId,
|
||||||
previewMode: false,
|
previewMode: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
imageLoading: false,
|
imageLoading: false,
|
||||||
|
@ -58,6 +68,8 @@ export class MarkdownTextArea extends Component<
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
|
this.handleLanguageChange = this.handleLanguageChange.bind(this);
|
||||||
|
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.tribute = setupTribute();
|
this.tribute = setupTribute();
|
||||||
}
|
}
|
||||||
|
@ -70,8 +82,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 = Some(textarea.value);
|
this.setState({ content: Some(textarea.value) });
|
||||||
this.setState(this.state);
|
|
||||||
autosize.update(textarea);
|
autosize.update(textarea);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -96,10 +107,7 @@ export class MarkdownTextArea extends Component<
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: MarkdownTextAreaProps) {
|
componentWillReceiveProps(nextProps: MarkdownTextAreaProps) {
|
||||||
if (nextProps.finished) {
|
if (nextProps.finished) {
|
||||||
this.state.previewMode = false;
|
this.setState({ previewMode: false, loading: false, content: None });
|
||||||
this.state.loading = false;
|
|
||||||
this.state.content = None;
|
|
||||||
this.setState(this.state);
|
|
||||||
if (this.props.replyType) {
|
if (this.props.replyType) {
|
||||||
this.props.onReplyCancel();
|
this.props.onReplyCancel();
|
||||||
}
|
}
|
||||||
|
@ -108,7 +116,6 @@ export class MarkdownTextArea extends Component<
|
||||||
let form: any = document.getElementById(this.formId);
|
let form: any = document.getElementById(this.formId);
|
||||||
form.reset();
|
form.reset();
|
||||||
setTimeout(() => autosize.update(textarea), 10);
|
setTimeout(() => autosize.update(textarea), 10);
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +132,7 @@ export class MarkdownTextArea extends Component<
|
||||||
}
|
}
|
||||||
message={i18n.t("block_leaving")}
|
message={i18n.t("block_leaving")}
|
||||||
/>
|
/>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div className={`col-sm-12`}>
|
<div className={`col-sm-12`}>
|
||||||
<textarea
|
<textarea
|
||||||
id={this.id}
|
id={this.id}
|
||||||
|
@ -150,17 +157,29 @@ export class MarkdownTextArea extends Component<
|
||||||
none: <></>,
|
none: <></>,
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<label class="sr-only" htmlFor={this.id}>
|
<label className="sr-only" htmlFor={this.id}>
|
||||||
{i18n.t("body")}
|
{i18n.t("body")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
{this.props.showLanguage && (
|
||||||
<div class="col-sm-12 d-flex flex-wrap">
|
<div className="row justify-content-end">
|
||||||
|
<div className="col-sm-8">
|
||||||
|
<LanguageSelect
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
|
selectedLanguageIds={this.state.languageId.map(Array.of)}
|
||||||
|
multiple={false}
|
||||||
|
onChange={this.handleLanguageChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-sm-12 d-flex flex-wrap">
|
||||||
{this.props.buttonTitle.match({
|
{this.props.buttonTitle.match({
|
||||||
some: buttonTitle => (
|
some: buttonTitle => (
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-sm btn-secondary mr-2"
|
className="btn btn-sm btn-secondary mr-2"
|
||||||
disabled={this.props.disabled || this.state.loading}
|
disabled={this.props.disabled || this.state.loading}
|
||||||
>
|
>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
|
@ -175,7 +194,7 @@ export class MarkdownTextArea extends Component<
|
||||||
{this.props.replyType && (
|
{this.props.replyType && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-sm btn-secondary mr-2"
|
className="btn btn-sm btn-secondary mr-2"
|
||||||
onClick={linkEvent(this, this.handleReplyCancel)}
|
onClick={linkEvent(this, this.handleReplyCancel)}
|
||||||
>
|
>
|
||||||
{i18n.t("cancel")}
|
{i18n.t("cancel")}
|
||||||
|
@ -192,9 +211,9 @@ export class MarkdownTextArea extends Component<
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{/* A flex expander */}
|
{/* A flex expander */}
|
||||||
<div class="flex-grow-1"></div>
|
<div className="flex-grow-1"></div>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm text-muted"
|
className="btn btn-sm text-muted"
|
||||||
data-tippy-content={i18n.t("bold")}
|
data-tippy-content={i18n.t("bold")}
|
||||||
aria-label={i18n.t("bold")}
|
aria-label={i18n.t("bold")}
|
||||||
onClick={linkEvent(this, this.handleInsertBold)}
|
onClick={linkEvent(this, this.handleInsertBold)}
|
||||||
|
@ -202,7 +221,7 @@ export class MarkdownTextArea extends Component<
|
||||||
<Icon icon="bold" classes="icon-inline" />
|
<Icon icon="bold" classes="icon-inline" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm text-muted"
|
className="btn btn-sm text-muted"
|
||||||
data-tippy-content={i18n.t("italic")}
|
data-tippy-content={i18n.t("italic")}
|
||||||
aria-label={i18n.t("italic")}
|
aria-label={i18n.t("italic")}
|
||||||
onClick={linkEvent(this, this.handleInsertItalic)}
|
onClick={linkEvent(this, this.handleInsertItalic)}
|
||||||
|
@ -210,14 +229,14 @@ export class MarkdownTextArea extends Component<
|
||||||
<Icon icon="italic" classes="icon-inline" />
|
<Icon icon="italic" classes="icon-inline" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm text-muted"
|
className="btn btn-sm text-muted"
|
||||||
data-tippy-content={i18n.t("link")}
|
data-tippy-content={i18n.t("link")}
|
||||||
aria-label={i18n.t("link")}
|
aria-label={i18n.t("link")}
|
||||||
onClick={linkEvent(this, this.handleInsertLink)}
|
onClick={linkEvent(this, this.handleInsertLink)}
|
||||||
>
|
>
|
||||||
<Icon icon="link" classes="icon-inline" />
|
<Icon icon="link" classes="icon-inline" />
|
||||||
</button>
|
</button>
|
||||||
<form class="btn btn-sm text-muted font-weight-bold">
|
<form className="btn btn-sm text-muted font-weight-bold">
|
||||||
<label
|
<label
|
||||||
htmlFor={`file-upload-${this.id}`}
|
htmlFor={`file-upload-${this.id}`}
|
||||||
className={`mb-0 ${
|
className={`mb-0 ${
|
||||||
|
@ -236,13 +255,13 @@ export class MarkdownTextArea extends Component<
|
||||||
type="file"
|
type="file"
|
||||||
accept="image/*,video/*"
|
accept="image/*,video/*"
|
||||||
name="file"
|
name="file"
|
||||||
class="d-none"
|
className="d-none"
|
||||||
disabled={UserService.Instance.myUserInfo.isNone()}
|
disabled={UserService.Instance.myUserInfo.isNone()}
|
||||||
onChange={linkEvent(this, this.handleImageUpload)}
|
onChange={linkEvent(this, this.handleImageUpload)}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm text-muted"
|
className="btn btn-sm text-muted"
|
||||||
data-tippy-content={i18n.t("header")}
|
data-tippy-content={i18n.t("header")}
|
||||||
aria-label={i18n.t("header")}
|
aria-label={i18n.t("header")}
|
||||||
onClick={linkEvent(this, this.handleInsertHeader)}
|
onClick={linkEvent(this, this.handleInsertHeader)}
|
||||||
|
@ -250,7 +269,7 @@ export class MarkdownTextArea extends Component<
|
||||||
<Icon icon="header" classes="icon-inline" />
|
<Icon icon="header" classes="icon-inline" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm text-muted"
|
className="btn btn-sm text-muted"
|
||||||
data-tippy-content={i18n.t("strikethrough")}
|
data-tippy-content={i18n.t("strikethrough")}
|
||||||
aria-label={i18n.t("strikethrough")}
|
aria-label={i18n.t("strikethrough")}
|
||||||
onClick={linkEvent(this, this.handleInsertStrikethrough)}
|
onClick={linkEvent(this, this.handleInsertStrikethrough)}
|
||||||
|
@ -258,7 +277,7 @@ export class MarkdownTextArea extends Component<
|
||||||
<Icon icon="strikethrough" classes="icon-inline" />
|
<Icon icon="strikethrough" classes="icon-inline" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm text-muted"
|
className="btn btn-sm text-muted"
|
||||||
data-tippy-content={i18n.t("quote")}
|
data-tippy-content={i18n.t("quote")}
|
||||||
aria-label={i18n.t("quote")}
|
aria-label={i18n.t("quote")}
|
||||||
onClick={linkEvent(this, this.handleInsertQuote)}
|
onClick={linkEvent(this, this.handleInsertQuote)}
|
||||||
|
@ -266,7 +285,7 @@ export class MarkdownTextArea extends Component<
|
||||||
<Icon icon="format_quote" classes="icon-inline" />
|
<Icon icon="format_quote" classes="icon-inline" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm text-muted"
|
className="btn btn-sm text-muted"
|
||||||
data-tippy-content={i18n.t("list")}
|
data-tippy-content={i18n.t("list")}
|
||||||
aria-label={i18n.t("list")}
|
aria-label={i18n.t("list")}
|
||||||
onClick={linkEvent(this, this.handleInsertList)}
|
onClick={linkEvent(this, this.handleInsertList)}
|
||||||
|
@ -274,7 +293,7 @@ export class MarkdownTextArea extends Component<
|
||||||
<Icon icon="list" classes="icon-inline" />
|
<Icon icon="list" classes="icon-inline" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm text-muted"
|
className="btn btn-sm text-muted"
|
||||||
data-tippy-content={i18n.t("code")}
|
data-tippy-content={i18n.t("code")}
|
||||||
aria-label={i18n.t("code")}
|
aria-label={i18n.t("code")}
|
||||||
onClick={linkEvent(this, this.handleInsertCode)}
|
onClick={linkEvent(this, this.handleInsertCode)}
|
||||||
|
@ -282,7 +301,7 @@ export class MarkdownTextArea extends Component<
|
||||||
<Icon icon="code" classes="icon-inline" />
|
<Icon icon="code" classes="icon-inline" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm text-muted"
|
className="btn btn-sm text-muted"
|
||||||
data-tippy-content={i18n.t("subscript")}
|
data-tippy-content={i18n.t("subscript")}
|
||||||
aria-label={i18n.t("subscript")}
|
aria-label={i18n.t("subscript")}
|
||||||
onClick={linkEvent(this, this.handleInsertSubscript)}
|
onClick={linkEvent(this, this.handleInsertSubscript)}
|
||||||
|
@ -290,7 +309,7 @@ export class MarkdownTextArea extends Component<
|
||||||
<Icon icon="subscript" classes="icon-inline" />
|
<Icon icon="subscript" classes="icon-inline" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm text-muted"
|
className="btn btn-sm text-muted"
|
||||||
data-tippy-content={i18n.t("superscript")}
|
data-tippy-content={i18n.t("superscript")}
|
||||||
aria-label={i18n.t("superscript")}
|
aria-label={i18n.t("superscript")}
|
||||||
onClick={linkEvent(this, this.handleInsertSuperscript)}
|
onClick={linkEvent(this, this.handleInsertSuperscript)}
|
||||||
|
@ -298,7 +317,7 @@ export class MarkdownTextArea extends Component<
|
||||||
<Icon icon="superscript" classes="icon-inline" />
|
<Icon icon="superscript" classes="icon-inline" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm text-muted"
|
className="btn btn-sm text-muted"
|
||||||
data-tippy-content={i18n.t("spoiler")}
|
data-tippy-content={i18n.t("spoiler")}
|
||||||
aria-label={i18n.t("spoiler")}
|
aria-label={i18n.t("spoiler")}
|
||||||
onClick={linkEvent(this, this.handleInsertSpoiler)}
|
onClick={linkEvent(this, this.handleInsertSpoiler)}
|
||||||
|
@ -307,7 +326,7 @@ export class MarkdownTextArea extends Component<
|
||||||
</button>
|
</button>
|
||||||
<a
|
<a
|
||||||
href={markdownHelpUrl}
|
href={markdownHelpUrl}
|
||||||
class="btn btn-sm text-muted font-weight-bold"
|
className="btn btn-sm text-muted font-weight-bold"
|
||||||
title={i18n.t("formatting_help")}
|
title={i18n.t("formatting_help")}
|
||||||
rel={relTags}
|
rel={relTags}
|
||||||
>
|
>
|
||||||
|
@ -338,8 +357,7 @@ export class MarkdownTextArea extends Component<
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("images[]", file);
|
formData.append("images[]", file);
|
||||||
|
|
||||||
i.state.imageLoading = true;
|
i.setState({ imageLoading: true });
|
||||||
i.setState(i.state);
|
|
||||||
|
|
||||||
fetch(pictrsUri, {
|
fetch(pictrsUri, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -355,15 +373,16 @@ 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})`;
|
||||||
i.state.content = Some(
|
i.setState({
|
||||||
i.state.content.match({
|
content: Some(
|
||||||
some: content => `${content}\n${imageMarkdown}`,
|
i.state.content.match({
|
||||||
none: imageMarkdown,
|
some: content => `${content}\n${imageMarkdown}`,
|
||||||
})
|
none: imageMarkdown,
|
||||||
);
|
})
|
||||||
i.state.imageLoading = false;
|
),
|
||||||
|
imageLoading: false,
|
||||||
|
});
|
||||||
i.contentChange();
|
i.contentChange();
|
||||||
i.setState(i.state);
|
|
||||||
let textarea: any = document.getElementById(i.id);
|
let textarea: any = document.getElementById(i.id);
|
||||||
autosize.update(textarea);
|
autosize.update(textarea);
|
||||||
pictrsDeleteToast(
|
pictrsDeleteToast(
|
||||||
|
@ -373,14 +392,12 @@ export class MarkdownTextArea extends Component<
|
||||||
deleteUrl
|
deleteUrl
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
i.state.imageLoading = false;
|
i.setState({ imageLoading: false });
|
||||||
i.setState(i.state);
|
|
||||||
toast(JSON.stringify(res), "danger");
|
toast(JSON.stringify(res), "danger");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
i.state.imageLoading = false;
|
i.setState({ imageLoading: false });
|
||||||
i.setState(i.state);
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
toast(error, "danger");
|
toast(error, "danger");
|
||||||
});
|
});
|
||||||
|
@ -393,22 +410,27 @@ export class MarkdownTextArea extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleContentChange(i: MarkdownTextArea, event: any) {
|
handleContentChange(i: MarkdownTextArea, event: any) {
|
||||||
i.state.content = Some(event.target.value);
|
i.setState({ content: Some(event.target.value) });
|
||||||
i.contentChange();
|
i.contentChange();
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePreviewToggle(i: MarkdownTextArea, event: any) {
|
handlePreviewToggle(i: MarkdownTextArea, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.previewMode = !i.state.previewMode;
|
i.setState({ previewMode: !i.state.previewMode });
|
||||||
i.setState(i.state);
|
}
|
||||||
|
|
||||||
|
handleLanguageChange(val: number[]) {
|
||||||
|
this.setState({ languageId: Some(val[0]) });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(i: MarkdownTextArea, event: any) {
|
handleSubmit(i: MarkdownTextArea, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.loading = true;
|
i.setState({ loading: true });
|
||||||
i.setState(i.state);
|
let msg = {
|
||||||
let msg = { val: toUndefined(i.state.content), formId: i.formId };
|
val: i.state.content,
|
||||||
|
formId: i.formId,
|
||||||
|
languageId: i.state.languageId,
|
||||||
|
};
|
||||||
i.props.onSubmit(msg);
|
i.props.onSubmit(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,27 +446,28 @@ export class MarkdownTextArea extends Component<
|
||||||
let end: number = textarea.selectionEnd;
|
let end: number = textarea.selectionEnd;
|
||||||
|
|
||||||
if (i.state.content.isNone()) {
|
if (i.state.content.isNone()) {
|
||||||
i.state.content = Some("");
|
i.setState({ content: Some("") });
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = i.state.content.unwrap();
|
let content = i.state.content.unwrap();
|
||||||
|
|
||||||
if (start !== end) {
|
if (start !== end) {
|
||||||
let selectedText = content.substring(start, end);
|
let selectedText = content.substring(start, end);
|
||||||
i.state.content = Some(
|
i.setState({
|
||||||
`${content.substring(0, start)}[${selectedText}]()${content.substring(
|
content: Some(
|
||||||
end
|
`${content.substring(0, start)}[${selectedText}]()${content.substring(
|
||||||
)}`
|
end
|
||||||
);
|
)}`
|
||||||
|
),
|
||||||
|
});
|
||||||
textarea.focus();
|
textarea.focus();
|
||||||
setTimeout(() => (textarea.selectionEnd = end + 3), 10);
|
setTimeout(() => (textarea.selectionEnd = end + 3), 10);
|
||||||
} else {
|
} else {
|
||||||
i.state.content = Some(`${content} []()`);
|
i.setState({ content: Some(`${content} []()`) });
|
||||||
textarea.focus();
|
textarea.focus();
|
||||||
setTimeout(() => (textarea.selectionEnd -= 1), 10);
|
setTimeout(() => (textarea.selectionEnd -= 1), 10);
|
||||||
}
|
}
|
||||||
i.contentChange();
|
i.contentChange();
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
simpleSurround(chars: string) {
|
simpleSurround(chars: string) {
|
||||||
|
@ -461,7 +484,7 @@ export class MarkdownTextArea extends Component<
|
||||||
emptyChars = "___"
|
emptyChars = "___"
|
||||||
) {
|
) {
|
||||||
if (this.state.content.isNone()) {
|
if (this.state.content.isNone()) {
|
||||||
this.state.content = Some("");
|
this.setState({ 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;
|
||||||
|
@ -471,19 +494,20 @@ export class MarkdownTextArea extends Component<
|
||||||
|
|
||||||
if (start !== end) {
|
if (start !== end) {
|
||||||
let selectedText = content.substring(start, end);
|
let selectedText = content.substring(start, end);
|
||||||
this.state.content = Some(
|
this.setState({
|
||||||
`${content.substring(
|
content: Some(
|
||||||
0,
|
`${content.substring(
|
||||||
start
|
0,
|
||||||
)}${beforeChars}${selectedText}${afterChars}${content.substring(end)}`
|
start
|
||||||
);
|
)}${beforeChars}${selectedText}${afterChars}${content.substring(end)}`
|
||||||
|
),
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.state.content = Some(
|
this.setState({
|
||||||
`${content}${beforeChars}${emptyChars}${afterChars}`
|
content: Some(`${content}${beforeChars}${emptyChars}${afterChars}`),
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
this.contentChange();
|
this.contentChange();
|
||||||
this.setState(this.state);
|
|
||||||
|
|
||||||
textarea.focus();
|
textarea.focus();
|
||||||
|
|
||||||
|
@ -555,9 +579,11 @@ export class MarkdownTextArea extends Component<
|
||||||
|
|
||||||
simpleInsert(chars: string) {
|
simpleInsert(chars: string) {
|
||||||
if (this.state.content.isNone()) {
|
if (this.state.content.isNone()) {
|
||||||
this.state.content = Some(`${chars} `);
|
this.setState({ content: Some(`${chars} `) });
|
||||||
} else {
|
} else {
|
||||||
this.state.content = Some(`${this.state.content.unwrap()}\n${chars} `);
|
this.setState({
|
||||||
|
content: Some(`${this.state.content.unwrap()}\n${chars} `),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let textarea: any = document.getElementById(this.id);
|
let textarea: any = document.getElementById(this.id);
|
||||||
|
@ -566,7 +592,6 @@ export class MarkdownTextArea extends Component<
|
||||||
autosize.update(textarea);
|
autosize.update(textarea);
|
||||||
}, 10);
|
}, 10);
|
||||||
this.contentChange();
|
this.contentChange();
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInsertSpoiler(i: MarkdownTextArea, event: any) {
|
handleInsertSpoiler(i: MarkdownTextArea, event: any) {
|
||||||
|
@ -586,13 +611,14 @@ export class MarkdownTextArea extends Component<
|
||||||
.map(t => `> ${t}`)
|
.map(t => `> ${t}`)
|
||||||
.join("\n") + "\n\n";
|
.join("\n") + "\n\n";
|
||||||
if (this.state.content.isNone()) {
|
if (this.state.content.isNone()) {
|
||||||
this.state.content = Some("");
|
this.setState({ content: Some("") });
|
||||||
} else {
|
} else {
|
||||||
this.state.content = Some(`${this.state.content.unwrap()}\n`);
|
this.setState({ content: Some(`${this.state.content.unwrap()}\n`) });
|
||||||
}
|
}
|
||||||
this.state.content = Some(`${this.state.content.unwrap()}${quotedText}`);
|
this.setState({
|
||||||
|
content: Some(`${this.state.content.unwrap()}${quotedText}`),
|
||||||
|
});
|
||||||
this.contentChange();
|
this.contentChange();
|
||||||
this.setState(this.state);
|
|
||||||
// Not sure why this needs a delay
|
// Not sure why this needs a delay
|
||||||
setTimeout(() => autosize.update(textarea), 10);
|
setTimeout(() => autosize.update(textarea), 10);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,16 +12,16 @@ export class Paginator extends Component<PaginatorProps, any> {
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="my-2">
|
<div className="my-2">
|
||||||
<button
|
<button
|
||||||
class="btn btn-secondary mr-2"
|
className="btn btn-secondary mr-2"
|
||||||
disabled={this.props.page == 1}
|
disabled={this.props.page == 1}
|
||||||
onClick={linkEvent(this, this.handlePrev)}
|
onClick={linkEvent(this, this.handlePrev)}
|
||||||
>
|
>
|
||||||
{i18n.t("prev")}
|
{i18n.t("prev")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
onClick={linkEvent(this, this.handleNext)}
|
onClick={linkEvent(this, this.handleNext)}
|
||||||
>
|
>
|
||||||
{i18n.t("next")}
|
{i18n.t("next")}
|
||||||
|
|
|
@ -88,18 +88,20 @@ export class RegistrationApplication extends Component<
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{this.state.denyExpanded && (
|
{this.state.denyExpanded && (
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label">
|
<label className="col-sm-2 col-form-label">
|
||||||
{i18n.t("deny_reason")}
|
{i18n.t("deny_reason")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.denyReason}
|
initialContent={this.state.denyReason}
|
||||||
|
initialLanguageId={None}
|
||||||
onContentChange={this.handleDenyReasonChange}
|
onContentChange={this.handleDenyReasonChange}
|
||||||
placeholder={None}
|
placeholder={None}
|
||||||
buttonTitle={None}
|
buttonTitle={None}
|
||||||
maxLength={None}
|
maxLength={None}
|
||||||
hideNavigationWarnings
|
hideNavigationWarnings
|
||||||
|
allLanguages={[]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -157,7 +159,6 @@ export class RegistrationApplication extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDenyReasonChange(val: string) {
|
handleDenyReasonChange(val: string) {
|
||||||
this.state.denyReason = Some(val);
|
this.setState({ denyReason: Some(val) });
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,23 +40,27 @@ export class SortSelect extends Component<SortSelectProps, SortSelectState> {
|
||||||
name={this.id}
|
name={this.id}
|
||||||
value={this.state.sort}
|
value={this.state.sort}
|
||||||
onChange={linkEvent(this, this.handleSortChange)}
|
onChange={linkEvent(this, this.handleSortChange)}
|
||||||
class="custom-select w-auto mr-2 mb-2"
|
className="custom-select w-auto mr-2 mb-2"
|
||||||
aria-label={i18n.t("sort_type")}
|
aria-label={i18n.t("sort_type")}
|
||||||
>
|
>
|
||||||
<option disabled aria-hidden="true">
|
<option disabled aria-hidden="true">
|
||||||
{i18n.t("sort_type")}
|
{i18n.t("sort_type")}
|
||||||
</option>
|
</option>
|
||||||
{!this.props.hideHot && [
|
{!this.props.hideHot && [
|
||||||
<option value={SortType.Hot}>{i18n.t("hot")}</option>,
|
<option key={SortType.Hot} value={SortType.Hot}>
|
||||||
<option value={SortType.Active}>{i18n.t("active")}</option>,
|
{i18n.t("hot")}
|
||||||
|
</option>,
|
||||||
|
<option key={SortType.Active} value={SortType.Active}>
|
||||||
|
{i18n.t("active")}
|
||||||
|
</option>,
|
||||||
]}
|
]}
|
||||||
<option value={SortType.New}>{i18n.t("new")}</option>
|
<option value={SortType.New}>{i18n.t("new")}</option>
|
||||||
<option value={SortType.Old}>{i18n.t("old")}</option>
|
<option value={SortType.Old}>{i18n.t("old")}</option>
|
||||||
{!this.props.hideMostComments && [
|
{!this.props.hideMostComments && [
|
||||||
<option value={SortType.MostComments}>
|
<option key={SortType.MostComments} value={SortType.MostComments}>
|
||||||
{i18n.t("most_comments")}
|
{i18n.t("most_comments")}
|
||||||
</option>,
|
</option>,
|
||||||
<option value={SortType.NewComments}>
|
<option key={SortType.NewComments} value={SortType.NewComments}>
|
||||||
{i18n.t("new_comments")}
|
{i18n.t("new_comments")}
|
||||||
</option>,
|
</option>,
|
||||||
]}
|
]}
|
||||||
|
|
|
@ -17,7 +17,7 @@ export const SYMBOLS = (
|
||||||
viewBox="0 0 196.52 196.52"
|
viewBox="0 0 196.52 196.52"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<g color="#000" font-weight="400" font-family="sans-serif">
|
<g color="#000" fontWeight="400" fontFamily="sans-serif">
|
||||||
<path
|
<path
|
||||||
d="M47.924 72.797a18.228 18.228 0 0 1-7.796 7.76l42.799 42.965 10.318-5.23zm56.453 56.67l-10.319 5.23 21.686 21.77a18.228 18.228 0 0 1 7.798-7.76z"
|
d="M47.924 72.797a18.228 18.228 0 0 1-7.796 7.76l42.799 42.965 10.318-5.23zm56.453 56.67l-10.319 5.23 21.686 21.77a18.228 18.228 0 0 1 7.798-7.76z"
|
||||||
overflow="visible"
|
overflow="visible"
|
||||||
|
@ -69,7 +69,7 @@ export const SYMBOLS = (
|
||||||
fill="#dbb210"
|
fill="#dbb210"
|
||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
<g transform="rotate(3.118 600.365 106.46)" fill-opacity=".996">
|
<g transform="rotate(3.118 600.365 106.46)" fillOpacity=".996">
|
||||||
<circle cx="106.266" cy="51.536" r="16.571" fill="#ffca00" />
|
<circle cx="106.266" cy="51.536" r="16.571" fill="#ffca00" />
|
||||||
<circle cx="171.428" cy="110.193" r="16.571" fill="#64ff00" />
|
<circle cx="171.428" cy="110.193" r="16.571" fill="#64ff00" />
|
||||||
<circle cx="135.764" cy="190.277" r="16.571" fill="#00a3ff" />
|
<circle cx="135.764" cy="190.277" r="16.571" fill="#00a3ff" />
|
||||||
|
|
|
@ -75,8 +75,11 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
// Only fetch the data if coming from another route
|
// Only fetch the data if coming from another route
|
||||||
if (this.isoData.path == this.context.router.route.match.url) {
|
if (this.isoData.path == this.context.router.route.match.url) {
|
||||||
let listRes = Some(this.isoData.routeData[0] as ListCommunitiesResponse);
|
let listRes = Some(this.isoData.routeData[0] as ListCommunitiesResponse);
|
||||||
this.state.listCommunitiesResponse = listRes;
|
this.state = {
|
||||||
this.state.loading = false;
|
...this.state,
|
||||||
|
listCommunitiesResponse: listRes,
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this.refetch();
|
this.refetch();
|
||||||
}
|
}
|
||||||
|
@ -114,7 +117,7 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
|
@ -127,10 +130,10 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
</h5>
|
</h5>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-md-6">
|
<div className="col-md-6">
|
||||||
<h4>{i18n.t("list_of_communities")}</h4>
|
<h4>{i18n.t("list_of_communities")}</h4>
|
||||||
<span class="mb-2">
|
<span className="mb-2">
|
||||||
<ListingTypeSelect
|
<ListingTypeSelect
|
||||||
type_={this.state.listingType}
|
type_={this.state.listingType}
|
||||||
showLocal={showLocal(this.isoData)}
|
showLocal={showLocal(this.isoData)}
|
||||||
|
@ -139,24 +142,27 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div className="col-md-6">
|
||||||
<div class="float-md-right">{this.searchForm()}</div>
|
<div className="float-md-right">{this.searchForm()}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div className="table-responsive">
|
||||||
<table id="community_table" class="table table-sm table-hover">
|
<table
|
||||||
<thead class="pointer">
|
id="community_table"
|
||||||
|
className="table table-sm table-hover"
|
||||||
|
>
|
||||||
|
<thead className="pointer">
|
||||||
<tr>
|
<tr>
|
||||||
<th>{i18n.t("name")}</th>
|
<th>{i18n.t("name")}</th>
|
||||||
<th class="text-right">{i18n.t("subscribers")}</th>
|
<th className="text-right">{i18n.t("subscribers")}</th>
|
||||||
<th class="text-right">
|
<th className="text-right">
|
||||||
{i18n.t("users")} / {i18n.t("month")}
|
{i18n.t("users")} / {i18n.t("month")}
|
||||||
</th>
|
</th>
|
||||||
<th class="text-right d-none d-lg-table-cell">
|
<th className="text-right d-none d-lg-table-cell">
|
||||||
{i18n.t("posts")}
|
{i18n.t("posts")}
|
||||||
</th>
|
</th>
|
||||||
<th class="text-right d-none d-lg-table-cell">
|
<th className="text-right d-none d-lg-table-cell">
|
||||||
{i18n.t("comments")}
|
{i18n.t("comments")}
|
||||||
</th>
|
</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
|
@ -167,26 +173,26 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
.map(l => l.communities)
|
.map(l => l.communities)
|
||||||
.unwrapOr([])
|
.unwrapOr([])
|
||||||
.map(cv => (
|
.map(cv => (
|
||||||
<tr>
|
<tr key={cv.community.id}>
|
||||||
<td>
|
<td>
|
||||||
<CommunityLink community={cv.community} />
|
<CommunityLink community={cv.community} />
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td className="text-right">
|
||||||
{numToSI(cv.counts.subscribers)}
|
{numToSI(cv.counts.subscribers)}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td className="text-right">
|
||||||
{numToSI(cv.counts.users_active_month)}
|
{numToSI(cv.counts.users_active_month)}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right d-none d-lg-table-cell">
|
<td className="text-right d-none d-lg-table-cell">
|
||||||
{numToSI(cv.counts.posts)}
|
{numToSI(cv.counts.posts)}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right d-none d-lg-table-cell">
|
<td className="text-right d-none d-lg-table-cell">
|
||||||
{numToSI(cv.counts.comments)}
|
{numToSI(cv.counts.comments)}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td className="text-right">
|
||||||
{cv.subscribed == SubscribedType.Subscribed && (
|
{cv.subscribed == SubscribedType.Subscribed && (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link d-inline-block"
|
className="btn btn-link d-inline-block"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
cv.community.id,
|
cv.community.id,
|
||||||
this.handleUnsubscribe
|
this.handleUnsubscribe
|
||||||
|
@ -197,7 +203,7 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
)}
|
)}
|
||||||
{cv.subscribed == SubscribedType.NotSubscribed && (
|
{cv.subscribed == SubscribedType.NotSubscribed && (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link d-inline-block"
|
className="btn btn-link d-inline-block"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
cv.community.id,
|
cv.community.id,
|
||||||
this.handleSubscribe
|
this.handleSubscribe
|
||||||
|
@ -207,7 +213,7 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{cv.subscribed == SubscribedType.Pending && (
|
{cv.subscribed == SubscribedType.Pending && (
|
||||||
<div class="text-warning d-inline-block">
|
<div className="text-warning d-inline-block">
|
||||||
{i18n.t("subscribe_pending")}
|
{i18n.t("subscribe_pending")}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -230,23 +236,23 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
searchForm() {
|
searchForm() {
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
class="form-inline"
|
className="form-inline"
|
||||||
onSubmit={linkEvent(this, this.handleSearchSubmit)}
|
onSubmit={linkEvent(this, this.handleSearchSubmit)}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="communities-search"
|
id="communities-search"
|
||||||
class="form-control mr-2 mb-2"
|
className="form-control mr-2 mb-2"
|
||||||
value={this.state.searchText}
|
value={this.state.searchText}
|
||||||
placeholder={`${i18n.t("search")}...`}
|
placeholder={`${i18n.t("search")}...`}
|
||||||
onInput={linkEvent(this, this.handleSearchChange)}
|
onInput={linkEvent(this, this.handleSearchChange)}
|
||||||
required
|
required
|
||||||
minLength={3}
|
minLength={3}
|
||||||
/>
|
/>
|
||||||
<label class="sr-only" htmlFor="communities-search">
|
<label className="sr-only" htmlFor="communities-search">
|
||||||
{i18n.t("search")}
|
{i18n.t("search")}
|
||||||
</label>
|
</label>
|
||||||
<button type="submit" class="btn btn-secondary mr-2 mb-2">
|
<button type="submit" className="btn btn-secondary mr-2 mb-2">
|
||||||
<span>{i18n.t("search")}</span>
|
<span>{i18n.t("search")}</span>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
@ -343,10 +349,8 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
msg,
|
msg,
|
||||||
ListCommunitiesResponse
|
ListCommunitiesResponse
|
||||||
);
|
);
|
||||||
this.state.listCommunitiesResponse = Some(data);
|
this.setState({ listCommunitiesResponse: Some(data), loading: false });
|
||||||
this.state.loading = false;
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.setState(this.state);
|
|
||||||
} else if (op == UserOperation.FollowCommunity) {
|
} else if (op == UserOperation.FollowCommunity) {
|
||||||
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
||||||
this.state.listCommunitiesResponse.match({
|
this.state.listCommunitiesResponse.match({
|
||||||
|
|
|
@ -73,9 +73,14 @@ 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);
|
||||||
|
|
||||||
this.props.community_view.match({
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
some: cv => {
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
this.state.communityForm = new CreateCommunity({
|
|
||||||
|
if (this.props.community_view.isSome()) {
|
||||||
|
let cv = this.props.community_view.unwrap();
|
||||||
|
this.state = {
|
||||||
|
...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,
|
||||||
|
@ -86,13 +91,9 @@ export class CommunityForm extends Component<
|
||||||
cv.community.posting_restricted_to_mods
|
cv.community.posting_restricted_to_mods
|
||||||
),
|
),
|
||||||
auth: undefined,
|
auth: undefined,
|
||||||
});
|
}),
|
||||||
},
|
};
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
|
@ -127,24 +128,24 @@ export class CommunityForm extends Component<
|
||||||
/>
|
/>
|
||||||
<form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
|
<form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
|
||||||
{this.props.community_view.isNone() && (
|
{this.props.community_view.isNone() && (
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label
|
<label
|
||||||
class="col-12 col-sm-2 col-form-label"
|
className="col-12 col-sm-2 col-form-label"
|
||||||
htmlFor="community-name"
|
htmlFor="community-name"
|
||||||
>
|
>
|
||||||
{i18n.t("name")}
|
{i18n.t("name")}
|
||||||
<span
|
<span
|
||||||
class="position-absolute pointer unselectable ml-2 text-muted"
|
className="position-absolute pointer unselectable ml-2 text-muted"
|
||||||
data-tippy-content={i18n.t("name_explain")}
|
data-tippy-content={i18n.t("name_explain")}
|
||||||
>
|
>
|
||||||
<Icon icon="help-circle" classes="icon-inline" />
|
<Icon icon="help-circle" classes="icon-inline" />
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<div class="col-12 col-sm-10">
|
<div className="col-12 col-sm-10">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="community-name"
|
id="community-name"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
value={this.state.communityForm.name}
|
value={this.state.communityForm.name}
|
||||||
onInput={linkEvent(this, this.handleCommunityNameChange)}
|
onInput={linkEvent(this, this.handleCommunityNameChange)}
|
||||||
required
|
required
|
||||||
|
@ -155,35 +156,35 @@ export class CommunityForm extends Component<
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label
|
<label
|
||||||
class="col-12 col-sm-2 col-form-label"
|
className="col-12 col-sm-2 col-form-label"
|
||||||
htmlFor="community-title"
|
htmlFor="community-title"
|
||||||
>
|
>
|
||||||
{i18n.t("display_name")}
|
{i18n.t("display_name")}
|
||||||
<span
|
<span
|
||||||
class="position-absolute pointer unselectable ml-2 text-muted"
|
className="position-absolute pointer unselectable ml-2 text-muted"
|
||||||
data-tippy-content={i18n.t("display_name_explain")}
|
data-tippy-content={i18n.t("display_name_explain")}
|
||||||
>
|
>
|
||||||
<Icon icon="help-circle" classes="icon-inline" />
|
<Icon icon="help-circle" classes="icon-inline" />
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<div class="col-12 col-sm-10">
|
<div className="col-12 col-sm-10">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="community-title"
|
id="community-title"
|
||||||
value={this.state.communityForm.title}
|
value={this.state.communityForm.title}
|
||||||
onInput={linkEvent(this, this.handleCommunityTitleChange)}
|
onInput={linkEvent(this, this.handleCommunityTitleChange)}
|
||||||
class="form-control"
|
className="form-control"
|
||||||
required
|
required
|
||||||
minLength={3}
|
minLength={3}
|
||||||
maxLength={100}
|
maxLength={100}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-12 col-sm-2">{i18n.t("icon")}</label>
|
<label className="col-12 col-sm-2">{i18n.t("icon")}</label>
|
||||||
<div class="col-12 col-sm-10">
|
<div className="col-12 col-sm-10">
|
||||||
<ImageUploadForm
|
<ImageUploadForm
|
||||||
uploadTitle={i18n.t("upload_icon")}
|
uploadTitle={i18n.t("upload_icon")}
|
||||||
imageSrc={this.state.communityForm.icon}
|
imageSrc={this.state.communityForm.icon}
|
||||||
|
@ -193,9 +194,9 @@ export class CommunityForm extends Component<
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-12 col-sm-2">{i18n.t("banner")}</label>
|
<label className="col-12 col-sm-2">{i18n.t("banner")}</label>
|
||||||
<div class="col-12 col-sm-10">
|
<div className="col-12 col-sm-10">
|
||||||
<ImageUploadForm
|
<ImageUploadForm
|
||||||
uploadTitle={i18n.t("upload_banner")}
|
uploadTitle={i18n.t("upload_banner")}
|
||||||
imageSrc={this.state.communityForm.banner}
|
imageSrc={this.state.communityForm.banner}
|
||||||
|
@ -204,30 +205,32 @@ export class CommunityForm extends Component<
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-12 col-sm-2 col-form-label" htmlFor={this.id}>
|
<label className="col-12 col-sm-2 col-form-label" htmlFor={this.id}>
|
||||||
{i18n.t("sidebar")}
|
{i18n.t("sidebar")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-12 col-sm-10">
|
<div className="col-12 col-sm-10">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.communityForm.description}
|
initialContent={this.state.communityForm.description}
|
||||||
|
initialLanguageId={None}
|
||||||
placeholder={Some("description")}
|
placeholder={Some("description")}
|
||||||
buttonTitle={None}
|
buttonTitle={None}
|
||||||
maxLength={None}
|
maxLength={None}
|
||||||
onContentChange={this.handleCommunityDescriptionChange}
|
onContentChange={this.handleCommunityDescriptionChange}
|
||||||
|
allLanguages={[]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{this.props.enableNsfw && (
|
{this.props.enableNsfw && (
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<legend class="col-form-label col-sm-2 pt-0">
|
<legend className="col-form-label col-sm-2 pt-0">
|
||||||
{i18n.t("nsfw")}
|
{i18n.t("nsfw")}
|
||||||
</legend>
|
</legend>
|
||||||
<div class="col-10">
|
<div className="col-10">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input position-static"
|
className="form-check-input position-static"
|
||||||
id="community-nsfw"
|
id="community-nsfw"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(this.state.communityForm.nsfw)}
|
checked={toUndefined(this.state.communityForm.nsfw)}
|
||||||
|
@ -237,14 +240,14 @@ export class CommunityForm extends Component<
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<legend class="col-form-label col-6 pt-0">
|
<legend className="col-form-label col-6 pt-0">
|
||||||
{i18n.t("only_mods_can_post_in_community")}
|
{i18n.t("only_mods_can_post_in_community")}
|
||||||
</legend>
|
</legend>
|
||||||
<div class="col-6">
|
<div className="col-6">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input position-static"
|
className="form-check-input position-static"
|
||||||
id="community-only-mods-can-post"
|
id="community-only-mods-can-post"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={toUndefined(
|
||||||
|
@ -258,11 +261,11 @@ export class CommunityForm extends Component<
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-secondary mr-2"
|
className="btn btn-secondary mr-2"
|
||||||
disabled={this.state.loading}
|
disabled={this.state.loading}
|
||||||
>
|
>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
|
@ -276,7 +279,7 @@ export class CommunityForm extends Component<
|
||||||
{this.props.community_view.isSome() && (
|
{this.props.community_view.isSome() && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
onClick={linkEvent(this, this.handleCancel)}
|
onClick={linkEvent(this, this.handleCancel)}
|
||||||
>
|
>
|
||||||
{i18n.t("cancel")}
|
{i18n.t("cancel")}
|
||||||
|
@ -291,7 +294,7 @@ export class CommunityForm extends Component<
|
||||||
|
|
||||||
handleCreateCommunitySubmit(i: CommunityForm, event: any) {
|
handleCreateCommunitySubmit(i: CommunityForm, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.loading = true;
|
i.setState({ loading: true });
|
||||||
let cForm = i.state.communityForm;
|
let cForm = i.state.communityForm;
|
||||||
cForm.auth = auth().unwrap();
|
cForm.auth = auth().unwrap();
|
||||||
|
|
||||||
|
@ -330,17 +333,18 @@ export class CommunityForm extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommunityDescriptionChange(val: string) {
|
handleCommunityDescriptionChange(val: string) {
|
||||||
this.state.communityForm.description = Some(val);
|
this.setState(s => ((s.communityForm.description = Some(val)), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommunityNsfwChange(i: CommunityForm, event: any) {
|
handleCommunityNsfwChange(i: CommunityForm, event: any) {
|
||||||
i.state.communityForm.nsfw = event.target.checked;
|
i.state.communityForm.nsfw = Some(event.target.checked);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommunityPostingRestrictedToMods(i: CommunityForm, event: any) {
|
handleCommunityPostingRestrictedToMods(i: CommunityForm, event: any) {
|
||||||
i.state.communityForm.posting_restricted_to_mods = event.target.checked;
|
i.state.communityForm.posting_restricted_to_mods = Some(
|
||||||
|
event.target.checked
|
||||||
|
);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,23 +353,19 @@ export class CommunityForm extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleIconUpload(url: string) {
|
handleIconUpload(url: string) {
|
||||||
this.state.communityForm.icon = Some(url);
|
this.setState(s => ((s.communityForm.icon = Some(url)), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleIconRemove() {
|
handleIconRemove() {
|
||||||
this.state.communityForm.icon = Some("");
|
this.setState(s => ((s.communityForm.icon = Some("")), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerUpload(url: string) {
|
handleBannerUpload(url: string) {
|
||||||
this.state.communityForm.banner = Some(url);
|
this.setState(s => ((s.communityForm.banner = Some(url)), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerRemove() {
|
handleBannerRemove() {
|
||||||
this.state.communityForm.banner = Some("");
|
this.setState(s => ((s.communityForm.banner = Some("")), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -374,12 +374,10 @@ export class CommunityForm extends Component<
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
// Errors handled by top level pages
|
// Errors handled by top level pages
|
||||||
// toast(i18n.t(msg.error), "danger");
|
// toast(i18n.t(msg.error), "danger");
|
||||||
this.state.loading = false;
|
this.setState({ loading: false });
|
||||||
this.setState(this.state);
|
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.CreateCommunity) {
|
} else if (op == UserOperation.CreateCommunity) {
|
||||||
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
||||||
this.state.loading = false;
|
|
||||||
this.props.onCreate(data.community_view);
|
this.props.onCreate(data.community_view);
|
||||||
|
|
||||||
// Update myUserInfo
|
// Update myUserInfo
|
||||||
|
@ -401,7 +399,7 @@ export class CommunityForm extends Component<
|
||||||
});
|
});
|
||||||
} else if (op == UserOperation.EditCommunity) {
|
} else if (op == UserOperation.EditCommunity) {
|
||||||
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
||||||
this.state.loading = false;
|
this.setState({ loading: false });
|
||||||
this.props.onEdit(data.community_view);
|
this.props.onEdit(data.community_view);
|
||||||
let community = data.community_view.community;
|
let community = data.community_view.community;
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ export class CommunityLink extends Component<CommunityLinkProps, any> {
|
||||||
some: icon => <PictrsImage src={icon} icon />,
|
some: icon => <PictrsImage src={icon} icon />,
|
||||||
none: <></>,
|
none: <></>,
|
||||||
})}
|
})}
|
||||||
<span class="overflow-wrap-anywhere">{displayName}</span>
|
<span className="overflow-wrap-anywhere">{displayName}</span>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,9 @@ import {
|
||||||
getDataTypeFromProps,
|
getDataTypeFromProps,
|
||||||
getPageFromProps,
|
getPageFromProps,
|
||||||
getSortTypeFromProps,
|
getSortTypeFromProps,
|
||||||
|
isPostBlocked,
|
||||||
notifyPost,
|
notifyPost,
|
||||||
|
nsfwCheck,
|
||||||
postToCommentSortType,
|
postToCommentSortType,
|
||||||
relTags,
|
relTags,
|
||||||
restoreScrollPosition,
|
restoreScrollPosition,
|
||||||
|
@ -139,24 +141,27 @@ 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 = Some(
|
this.state = {
|
||||||
this.isoData.routeData[0] as GetCommunityResponse
|
...this.state,
|
||||||
);
|
communityRes: Some(this.isoData.routeData[0] as GetCommunityResponse),
|
||||||
|
};
|
||||||
let postsRes = Some(this.isoData.routeData[1] as GetPostsResponse);
|
let postsRes = Some(this.isoData.routeData[1] as GetPostsResponse);
|
||||||
let commentsRes = Some(this.isoData.routeData[2] as GetCommentsResponse);
|
let commentsRes = Some(this.isoData.routeData[2] as GetCommentsResponse);
|
||||||
|
|
||||||
postsRes.match({
|
if (postsRes.isSome()) {
|
||||||
some: pvs => (this.state.posts = pvs.posts),
|
this.state = { ...this.state, posts: postsRes.unwrap().posts };
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
commentsRes.match({
|
|
||||||
some: cvs => (this.state.comments = cvs.comments),
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.state.communityLoading = false;
|
if (commentsRes.isSome()) {
|
||||||
this.state.postsLoading = false;
|
this.state = { ...this.state, comments: commentsRes.unwrap().comments };
|
||||||
this.state.commentsLoading = false;
|
}
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
communityLoading: false,
|
||||||
|
postsLoading: false,
|
||||||
|
commentsLoading: false,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this.fetchCommunity();
|
this.fetchCommunity();
|
||||||
this.fetchData();
|
this.fetchData();
|
||||||
|
@ -278,7 +283,7 @@ export class Community extends Component<any, State> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
{this.state.communityLoading ? (
|
{this.state.communityLoading ? (
|
||||||
<h5>
|
<h5>
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
|
@ -294,12 +299,12 @@ export class Community extends Component<any, State> {
|
||||||
image={res.community_view.community.icon}
|
image={res.community_view.community.icon}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12 col-md-8">
|
<div className="col-12 col-md-8">
|
||||||
{this.communityInfo()}
|
{this.communityInfo()}
|
||||||
<div class="d-block d-md-none">
|
<div className="d-block d-md-none">
|
||||||
<button
|
<button
|
||||||
class="btn btn-secondary d-inline-block mb-2 mr-3"
|
className="btn btn-secondary d-inline-block mb-2 mr-3"
|
||||||
onClick={linkEvent(this, this.handleShowSidebarMobile)}
|
onClick={linkEvent(this, this.handleShowSidebarMobile)}
|
||||||
>
|
>
|
||||||
{i18n.t("sidebar")}{" "}
|
{i18n.t("sidebar")}{" "}
|
||||||
|
@ -344,7 +349,7 @@ export class Community extends Component<any, State> {
|
||||||
onChange={this.handlePageChange}
|
onChange={this.handlePageChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-none d-md-block col-md-4">
|
<div className="d-none d-md-block col-md-4">
|
||||||
<Sidebar
|
<Sidebar
|
||||||
community_view={res.community_view}
|
community_view={res.community_view}
|
||||||
moderators={res.moderators}
|
moderators={res.moderators}
|
||||||
|
@ -388,6 +393,7 @@ export class Community extends Component<any, State> {
|
||||||
removeDuplicates
|
removeDuplicates
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
) : this.state.commentsLoading ? (
|
) : this.state.commentsLoading ? (
|
||||||
|
@ -404,6 +410,7 @@ export class Community extends Component<any, State> {
|
||||||
moderators={this.state.communityRes.map(r => r.moderators)}
|
moderators={this.state.communityRes.map(r => r.moderators)}
|
||||||
admins={Some(this.state.siteRes.admins)}
|
admins={Some(this.state.siteRes.admins)}
|
||||||
maxCommentsShown={None}
|
maxCommentsShown={None}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -413,9 +420,9 @@ export class Community extends Component<any, State> {
|
||||||
.map(r => r.community_view.community)
|
.map(r => r.community_view.community)
|
||||||
.match({
|
.match({
|
||||||
some: community => (
|
some: community => (
|
||||||
<div class="mb-2">
|
<div className="mb-2">
|
||||||
<BannerIconHeader banner={community.banner} icon={community.icon} />
|
<BannerIconHeader banner={community.banner} icon={community.icon} />
|
||||||
<h5 class="mb-0 overflow-wrap-anywhere">{community.title}</h5>
|
<h5 className="mb-0 overflow-wrap-anywhere">{community.title}</h5>
|
||||||
<CommunityLink
|
<CommunityLink
|
||||||
community={community}
|
community={community}
|
||||||
realLink
|
realLink
|
||||||
|
@ -434,14 +441,14 @@ export class Community extends Component<any, State> {
|
||||||
communityRSSUrl(r.community_view.community.actor_id, this.state.sort)
|
communityRSSUrl(r.community_view.community.actor_id, this.state.sort)
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div class="mb-3">
|
<div className="mb-3">
|
||||||
<span class="mr-3">
|
<span className="mr-3">
|
||||||
<DataTypeSelect
|
<DataTypeSelect
|
||||||
type_={this.state.dataType}
|
type_={this.state.dataType}
|
||||||
onChange={this.handleDataTypeChange}
|
onChange={this.handleDataTypeChange}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="mr-2">
|
<span className="mr-2">
|
||||||
<SortSelect sort={this.state.sort} onChange={this.handleSortChange} />
|
<SortSelect sort={this.state.sort} onChange={this.handleSortChange} />
|
||||||
</span>
|
</span>
|
||||||
{communityRss.match({
|
{communityRss.match({
|
||||||
|
@ -475,8 +482,7 @@ export class Community extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowSidebarMobile(i: Community) {
|
handleShowSidebarMobile(i: Community) {
|
||||||
i.state.showSidebarMobile = !i.state.showSidebarMobile;
|
i.setState({ showSidebarMobile: !i.state.showSidebarMobile });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUrl(paramUpdates: UrlParams) {
|
updateUrl(paramUpdates: UrlParams) {
|
||||||
|
@ -543,9 +549,7 @@ export class Community extends Component<any, State> {
|
||||||
this.fetchData();
|
this.fetchData();
|
||||||
} else if (op == UserOperation.GetCommunity) {
|
} else if (op == UserOperation.GetCommunity) {
|
||||||
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
||||||
this.state.communityRes = Some(data);
|
this.setState({ communityRes: Some(data), communityLoading: false });
|
||||||
this.state.communityLoading = false;
|
|
||||||
this.setState(this.state);
|
|
||||||
// TODO why is there no auth in this form?
|
// TODO why is there no auth in this form?
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.communityJoin({
|
wsClient.communityJoin({
|
||||||
|
@ -576,9 +580,7 @@ export class Community extends Component<any, State> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.GetPosts) {
|
} else if (op == UserOperation.GetPosts) {
|
||||||
let data = wsJsonToRes<GetPostsResponse>(msg, GetPostsResponse);
|
let data = wsJsonToRes<GetPostsResponse>(msg, GetPostsResponse);
|
||||||
this.state.posts = data.posts;
|
this.setState({ posts: data.posts, postsLoading: false });
|
||||||
this.state.postsLoading = false;
|
|
||||||
this.setState(this.state);
|
|
||||||
restoreScrollPosition(this.context);
|
restoreScrollPosition(this.context);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (
|
} else if (
|
||||||
|
@ -594,15 +596,24 @@ export class Community extends Component<any, State> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.CreatePost) {
|
} else if (op == UserOperation.CreatePost) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
||||||
this.state.posts.unshift(data.post_view);
|
|
||||||
|
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, you pass the nsfw check, and it isn't blocked
|
||||||
|
//
|
||||||
if (
|
if (
|
||||||
UserService.Instance.myUserInfo
|
this.state.page == 1 &&
|
||||||
.map(m => m.local_user_view.local_user.show_new_post_notifs)
|
nsfwCheck(data.post_view) &&
|
||||||
.unwrapOr(false)
|
!isPostBlocked(data.post_view)
|
||||||
) {
|
) {
|
||||||
notifyPost(data.post_view, this.context.router);
|
this.state.posts.unshift(data.post_view);
|
||||||
|
if (showPostNotifs) {
|
||||||
|
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, PostResponse);
|
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
||||||
createPostLikeFindRes(data.post_view, this.state.posts);
|
createPostLikeFindRes(data.post_view, this.state.posts);
|
||||||
|
@ -631,9 +642,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, GetCommentsResponse);
|
let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
|
||||||
this.state.comments = data.comments;
|
this.setState({ comments: data.comments, commentsLoading: false });
|
||||||
this.state.commentsLoading = false;
|
|
||||||
this.setState(this.state);
|
|
||||||
} else if (
|
} else if (
|
||||||
op == UserOperation.EditComment ||
|
op == UserOperation.EditComment ||
|
||||||
op == UserOperation.DeleteComment ||
|
op == UserOperation.DeleteComment ||
|
||||||
|
|
|
@ -56,7 +56,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
|
@ -68,8 +68,8 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
</h5>
|
</h5>
|
||||||
) : (
|
) : (
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
<div className="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||||
<h5>{i18n.t("create_community")}</h5>
|
<h5>{i18n.t("create_community")}</h5>
|
||||||
<CommunityForm
|
<CommunityForm
|
||||||
community_view={None}
|
community_view={None}
|
||||||
|
|
|
@ -91,16 +91,17 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
sidebar() {
|
sidebar() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div class="card border-secondary mb-3">
|
<div className="card border-secondary mb-3">
|
||||||
<div class="card-body">
|
<div className="card-body">
|
||||||
{this.communityTitle()}
|
{this.communityTitle()}
|
||||||
{this.adminButtons()}
|
{this.adminButtons()}
|
||||||
{this.subscribe()}
|
{this.subscribe()}
|
||||||
{this.canPost && this.createPost()}
|
{this.canPost && this.createPost()}
|
||||||
|
{this.blockCommunity()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card border-secondary mb-3">
|
<div className="card border-secondary mb-3">
|
||||||
<div class="card-body">
|
<div className="card-body">
|
||||||
{this.description()}
|
{this.description()}
|
||||||
{this.badges()}
|
{this.badges()}
|
||||||
{this.mods()}
|
{this.mods()}
|
||||||
|
@ -119,10 +120,10 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
{this.props.showIcon && (
|
{this.props.showIcon && (
|
||||||
<BannerIconHeader icon={community.icon} banner={community.banner} />
|
<BannerIconHeader icon={community.icon} banner={community.banner} />
|
||||||
)}
|
)}
|
||||||
<span class="mr-2">{community.title}</span>
|
<span className="mr-2">{community.title}</span>
|
||||||
{subscribed == SubscribedType.Subscribed && (
|
{subscribed == SubscribedType.Subscribed && (
|
||||||
<button
|
<button
|
||||||
class="btn btn-secondary btn-sm mr-2"
|
className="btn btn-secondary btn-sm mr-2"
|
||||||
onClick={linkEvent(this, this.handleUnsubscribe)}
|
onClick={linkEvent(this, this.handleUnsubscribe)}
|
||||||
>
|
>
|
||||||
<Icon icon="check" classes="icon-inline text-success mr-1" />
|
<Icon icon="check" classes="icon-inline text-success mr-1" />
|
||||||
|
@ -131,7 +132,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
)}
|
)}
|
||||||
{subscribed == SubscribedType.Pending && (
|
{subscribed == SubscribedType.Pending && (
|
||||||
<button
|
<button
|
||||||
class="btn btn-warning mr-2"
|
className="btn btn-warning mr-2"
|
||||||
onClick={linkEvent(this, this.handleUnsubscribe)}
|
onClick={linkEvent(this, this.handleUnsubscribe)}
|
||||||
>
|
>
|
||||||
{i18n.t("subscribe_pending")}
|
{i18n.t("subscribe_pending")}
|
||||||
|
@ -168,7 +169,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
let community_view = this.props.community_view;
|
let community_view = this.props.community_view;
|
||||||
let counts = community_view.counts;
|
let counts = community_view.counts;
|
||||||
return (
|
return (
|
||||||
<ul class="my-1 list-inline">
|
<ul className="my-1 list-inline">
|
||||||
<li className="list-inline-item badge badge-secondary">
|
<li className="list-inline-item badge badge-secondary">
|
||||||
{i18n.t("number_online", {
|
{i18n.t("number_online", {
|
||||||
count: this.props.online,
|
count: this.props.online,
|
||||||
|
@ -259,10 +260,10 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
|
|
||||||
mods() {
|
mods() {
|
||||||
return (
|
return (
|
||||||
<ul class="list-inline small">
|
<ul className="list-inline small">
|
||||||
<li class="list-inline-item">{i18n.t("mods")}: </li>
|
<li className="list-inline-item">{i18n.t("mods")}: </li>
|
||||||
{this.props.moderators.map(mod => (
|
{this.props.moderators.map(mod => (
|
||||||
<li class="list-inline-item">
|
<li key={mod.moderator.id} className="list-inline-item">
|
||||||
<PersonListing person={mod.moderator} />
|
<PersonListing person={mod.moderator} />
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
@ -273,53 +274,59 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
createPost() {
|
createPost() {
|
||||||
let cv = this.props.community_view;
|
let cv = this.props.community_view;
|
||||||
return (
|
return (
|
||||||
cv.subscribed == SubscribedType.Subscribed && (
|
<Link
|
||||||
<Link
|
className={`btn btn-secondary btn-block mb-2 ${
|
||||||
className={`btn btn-secondary btn-block mb-2 ${
|
cv.community.deleted || cv.community.removed ? "no-click" : ""
|
||||||
cv.community.deleted || cv.community.removed ? "no-click" : ""
|
}`}
|
||||||
}`}
|
to={`/create_post?community_id=${cv.community.id}`}
|
||||||
to={`/create_post?community_id=${cv.community.id}`}
|
>
|
||||||
>
|
{i18n.t("create_a_post")}
|
||||||
{i18n.t("create_a_post")}
|
</Link>
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe() {
|
subscribe() {
|
||||||
let community_view = this.props.community_view;
|
let community_view = this.props.community_view;
|
||||||
let blocked = this.props.community_view.blocked;
|
|
||||||
return (
|
return (
|
||||||
<div class="mb-2">
|
<div className="mb-2">
|
||||||
{community_view.subscribed == SubscribedType.NotSubscribed && (
|
{community_view.subscribed == SubscribedType.NotSubscribed && (
|
||||||
<>
|
<button
|
||||||
<button
|
className="btn btn-secondary btn-block"
|
||||||
class="btn btn-secondary btn-block"
|
onClick={linkEvent(this, this.handleSubscribe)}
|
||||||
onClick={linkEvent(this, this.handleSubscribe)}
|
>
|
||||||
>
|
{i18n.t("subscribe")}
|
||||||
{i18n.t("subscribe")}
|
</button>
|
||||||
</button>
|
|
||||||
{blocked ? (
|
|
||||||
<button
|
|
||||||
class="btn btn-danger btn-block"
|
|
||||||
onClick={linkEvent(this, this.handleUnblock)}
|
|
||||||
>
|
|
||||||
{i18n.t("unblock_community")}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
class="btn btn-danger btn-block"
|
|
||||||
onClick={linkEvent(this, this.handleBlock)}
|
|
||||||
>
|
|
||||||
{i18n.t("block_community")}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blockCommunity() {
|
||||||
|
let community_view = this.props.community_view;
|
||||||
|
let blocked = this.props.community_view.blocked;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mb-2">
|
||||||
|
{community_view.subscribed == SubscribedType.NotSubscribed &&
|
||||||
|
(blocked ? (
|
||||||
|
<button
|
||||||
|
className="btn btn-danger btn-block"
|
||||||
|
onClick={linkEvent(this, this.handleUnblock)}
|
||||||
|
>
|
||||||
|
{i18n.t("unblock_community")}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
className="btn btn-danger btn-block"
|
||||||
|
onClick={linkEvent(this, this.handleBlock)}
|
||||||
|
>
|
||||||
|
{i18n.t("block_community")}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
description() {
|
description() {
|
||||||
let description = this.props.community_view.community.description;
|
let description = this.props.community_view.community.description;
|
||||||
return description.match({
|
return description.match({
|
||||||
|
@ -334,12 +341,12 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
let community_view = this.props.community_view;
|
let community_view = this.props.community_view;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ul class="list-inline mb-1 text-muted font-weight-bold">
|
<ul className="list-inline mb-1 text-muted font-weight-bold">
|
||||||
{amMod(Some(this.props.moderators)) && (
|
{amMod(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"
|
className="btn btn-link text-muted d-inline-block"
|
||||||
onClick={linkEvent(this, this.handleEditClick)}
|
onClick={linkEvent(this, this.handleEditClick)}
|
||||||
data-tippy-content={i18n.t("edit")}
|
data-tippy-content={i18n.t("edit")}
|
||||||
aria-label={i18n.t("edit")}
|
aria-label={i18n.t("edit")}
|
||||||
|
@ -351,7 +358,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
(!this.state.showConfirmLeaveModTeam ? (
|
(!this.state.showConfirmLeaveModTeam ? (
|
||||||
<li className="list-inline-item-action">
|
<li className="list-inline-item-action">
|
||||||
<button
|
<button
|
||||||
class="btn btn-link text-muted d-inline-block"
|
className="btn btn-link text-muted d-inline-block"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleShowConfirmLeaveModTeamClick
|
this.handleShowConfirmLeaveModTeamClick
|
||||||
|
@ -367,7 +374,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
</li>
|
</li>
|
||||||
<li className="list-inline-item-action">
|
<li className="list-inline-item-action">
|
||||||
<button
|
<button
|
||||||
class="btn btn-link text-muted d-inline-block"
|
className="btn btn-link text-muted d-inline-block"
|
||||||
onClick={linkEvent(this, this.handleLeaveModTeamClick)}
|
onClick={linkEvent(this, this.handleLeaveModTeamClick)}
|
||||||
>
|
>
|
||||||
{i18n.t("yes")}
|
{i18n.t("yes")}
|
||||||
|
@ -375,7 +382,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
</li>
|
</li>
|
||||||
<li className="list-inline-item-action">
|
<li className="list-inline-item-action">
|
||||||
<button
|
<button
|
||||||
class="btn btn-link text-muted d-inline-block"
|
className="btn btn-link text-muted d-inline-block"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleCancelLeaveModTeamClick
|
this.handleCancelLeaveModTeamClick
|
||||||
|
@ -389,7 +396,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
{amTopMod(Some(this.props.moderators)) && (
|
{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"
|
className="btn btn-link text-muted d-inline-block"
|
||||||
onClick={linkEvent(this, this.handleDeleteClick)}
|
onClick={linkEvent(this, this.handleDeleteClick)}
|
||||||
data-tippy-content={
|
data-tippy-content={
|
||||||
!community_view.community.deleted
|
!community_view.community.deleted
|
||||||
|
@ -413,25 +420,25 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{amAdmin(Some(this.props.admins)) && (
|
{amAdmin() && (
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
{!this.props.community_view.community.removed ? (
|
{!this.props.community_view.community.removed ? (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link text-muted d-inline-block"
|
className="btn btn-link text-muted d-inline-block"
|
||||||
onClick={linkEvent(this, this.handleModRemoveShow)}
|
onClick={linkEvent(this, this.handleModRemoveShow)}
|
||||||
>
|
>
|
||||||
{i18n.t("remove")}
|
{i18n.t("remove")}
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link text-muted d-inline-block"
|
className="btn btn-link text-muted d-inline-block"
|
||||||
onClick={linkEvent(this, this.handleModRemoveSubmit)}
|
onClick={linkEvent(this, this.handleModRemoveSubmit)}
|
||||||
>
|
>
|
||||||
{i18n.t("restore")}
|
{i18n.t("restore")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
class="btn btn-link text-muted d-inline-block"
|
className="btn btn-link text-muted d-inline-block"
|
||||||
onClick={linkEvent(this, this.handlePurgeCommunityShow)}
|
onClick={linkEvent(this, this.handlePurgeCommunityShow)}
|
||||||
aria-label={i18n.t("purge_community")}
|
aria-label={i18n.t("purge_community")}
|
||||||
>
|
>
|
||||||
|
@ -442,14 +449,14 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
</ul>
|
</ul>
|
||||||
{this.state.showRemoveDialog && (
|
{this.state.showRemoveDialog && (
|
||||||
<form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleModRemoveSubmit)}>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<label class="col-form-label" htmlFor="remove-reason">
|
<label className="col-form-label" htmlFor="remove-reason">
|
||||||
{i18n.t("reason")}
|
{i18n.t("reason")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="remove-reason"
|
id="remove-reason"
|
||||||
class="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("optional")}
|
placeholder={i18n.t("optional")}
|
||||||
value={toUndefined(this.state.removeReason)}
|
value={toUndefined(this.state.removeReason)}
|
||||||
onInput={linkEvent(this, this.handleModRemoveReasonChange)}
|
onInput={linkEvent(this, this.handleModRemoveReasonChange)}
|
||||||
|
@ -460,8 +467,8 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
{/* <label class="col-form-label">Expires</label> */}
|
{/* <label class="col-form-label">Expires</label> */}
|
||||||
{/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
|
{/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.removeExpires} onInput={linkEvent(this, this.handleModRemoveExpiresChange)} /> */}
|
||||||
{/* </div> */}
|
{/* </div> */}
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<button type="submit" class="btn btn-secondary">
|
<button type="submit" className="btn btn-secondary">
|
||||||
{i18n.t("remove_community")}
|
{i18n.t("remove_community")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -469,29 +476,29 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
)}
|
)}
|
||||||
{this.state.showPurgeDialog && (
|
{this.state.showPurgeDialog && (
|
||||||
<form onSubmit={linkEvent(this, this.handlePurgeSubmit)}>
|
<form onSubmit={linkEvent(this, this.handlePurgeSubmit)}>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<PurgeWarning />
|
<PurgeWarning />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<label class="sr-only" htmlFor="purge-reason">
|
<label className="sr-only" htmlFor="purge-reason">
|
||||||
{i18n.t("reason")}
|
{i18n.t("reason")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="purge-reason"
|
id="purge-reason"
|
||||||
class="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("reason")}
|
placeholder={i18n.t("reason")}
|
||||||
value={toUndefined(this.state.purgeReason)}
|
value={toUndefined(this.state.purgeReason)}
|
||||||
onInput={linkEvent(this, this.handlePurgeReasonChange)}
|
onInput={linkEvent(this, this.handlePurgeReasonChange)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
{this.state.purgeLoading ? (
|
{this.state.purgeLoading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
aria-label={i18n.t("purge_community")}
|
aria-label={i18n.t("purge_community")}
|
||||||
>
|
>
|
||||||
{i18n.t("purge_community")}
|
{i18n.t("purge_community")}
|
||||||
|
@ -505,18 +512,15 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditClick(i: Sidebar) {
|
handleEditClick(i: Sidebar) {
|
||||||
i.state.showEdit = true;
|
i.setState({ showEdit: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditCommunity() {
|
handleEditCommunity() {
|
||||||
this.state.showEdit = false;
|
this.setState({ showEdit: false });
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditCancel() {
|
handleEditCancel() {
|
||||||
this.state.showEdit = false;
|
this.setState({ showEdit: false });
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteClick(i: Sidebar, event: any) {
|
handleDeleteClick(i: Sidebar, event: any) {
|
||||||
|
@ -530,8 +534,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowConfirmLeaveModTeamClick(i: Sidebar) {
|
handleShowConfirmLeaveModTeamClick(i: Sidebar) {
|
||||||
i.state.showConfirmLeaveModTeam = true;
|
i.setState({ showConfirmLeaveModTeam: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLeaveModTeamClick(i: Sidebar) {
|
handleLeaveModTeamClick(i: Sidebar) {
|
||||||
|
@ -544,16 +547,14 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
auth: auth().unwrap(),
|
auth: auth().unwrap(),
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.addModToCommunity(form));
|
WebSocketService.Instance.send(wsClient.addModToCommunity(form));
|
||||||
i.state.showConfirmLeaveModTeam = false;
|
i.setState({ showConfirmLeaveModTeam: false });
|
||||||
i.setState(i.state);
|
|
||||||
},
|
},
|
||||||
none: void 0,
|
none: void 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancelLeaveModTeamClick(i: Sidebar) {
|
handleCancelLeaveModTeamClick(i: Sidebar) {
|
||||||
i.state.showConfirmLeaveModTeam = false;
|
i.setState({ showConfirmLeaveModTeam: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnsubscribe(i: Sidebar, event: any) {
|
handleUnsubscribe(i: Sidebar, event: any) {
|
||||||
|
@ -599,23 +600,20 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
return (
|
return (
|
||||||
!this.props.community_view.community.posting_restricted_to_mods ||
|
!this.props.community_view.community.posting_restricted_to_mods ||
|
||||||
amMod(Some(this.props.moderators)) ||
|
amMod(Some(this.props.moderators)) ||
|
||||||
amAdmin(Some(this.props.admins))
|
amAdmin()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveShow(i: Sidebar) {
|
handleModRemoveShow(i: Sidebar) {
|
||||||
i.state.showRemoveDialog = true;
|
i.setState({ showRemoveDialog: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveReasonChange(i: Sidebar, event: any) {
|
handleModRemoveReasonChange(i: Sidebar, event: any) {
|
||||||
i.state.removeReason = Some(event.target.value);
|
i.setState({ removeReason: Some(event.target.value) });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveExpiresChange(i: Sidebar, event: any) {
|
handleModRemoveExpiresChange(i: Sidebar, event: any) {
|
||||||
i.state.removeExpires = Some(event.target.value);
|
i.setState({ removeExpires: Some(event.target.value) });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveSubmit(i: Sidebar, event: any) {
|
handleModRemoveSubmit(i: Sidebar, event: any) {
|
||||||
|
@ -629,19 +627,15 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.removeCommunity(removeForm));
|
WebSocketService.Instance.send(wsClient.removeCommunity(removeForm));
|
||||||
|
|
||||||
i.state.showRemoveDialog = false;
|
i.setState({ showRemoveDialog: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePurgeCommunityShow(i: Sidebar) {
|
handlePurgeCommunityShow(i: Sidebar) {
|
||||||
i.state.showPurgeDialog = true;
|
i.setState({ showPurgeDialog: true, showRemoveDialog: false });
|
||||||
i.state.showRemoveDialog = false;
|
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePurgeReasonChange(i: Sidebar, event: any) {
|
handlePurgeReasonChange(i: Sidebar, event: any) {
|
||||||
i.state.purgeReason = Some(event.target.value);
|
i.setState({ purgeReason: Some(event.target.value) });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePurgeSubmit(i: Sidebar, event: any) {
|
handlePurgeSubmit(i: Sidebar, event: any) {
|
||||||
|
@ -654,8 +648,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.purgeCommunity(form));
|
WebSocketService.Instance.send(wsClient.purgeCommunity(form));
|
||||||
|
|
||||||
i.state.purgeLoading = true;
|
i.setState({ purgeLoading: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBlock(i: Sidebar, event: any) {
|
handleBlock(i: Sidebar, event: any) {
|
||||||
|
|
|
@ -59,10 +59,11 @@ 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.banned = (
|
this.state = {
|
||||||
this.isoData.routeData[0] as BannedPersonsResponse
|
...this.state,
|
||||||
).banned;
|
banned: (this.isoData.routeData[0] as BannedPersonsResponse).banned,
|
||||||
this.state.loading = false;
|
loading: false,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.getBannedPersons({
|
wsClient.getBannedPersons({
|
||||||
|
@ -103,14 +104,14 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<h5>
|
<h5>
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
</h5>
|
</h5>
|
||||||
) : (
|
) : (
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12 col-md-6">
|
<div className="col-12 col-md-6">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
|
@ -127,7 +128,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
none: <></>,
|
none: <></>,
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-6">
|
<div className="col-12 col-md-6">
|
||||||
{this.admins()}
|
{this.admins()}
|
||||||
{this.bannedUsers()}
|
{this.bannedUsers()}
|
||||||
</div>
|
</div>
|
||||||
|
@ -141,9 +142,9 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h5>{capitalizeFirstLetter(i18n.t("admins"))}</h5>
|
<h5>{capitalizeFirstLetter(i18n.t("admins"))}</h5>
|
||||||
<ul class="list-unstyled">
|
<ul className="list-unstyled">
|
||||||
{this.state.siteRes.admins.map(admin => (
|
{this.state.siteRes.admins.map(admin => (
|
||||||
<li class="list-inline-item">
|
<li key={admin.person.id} className="list-inline-item">
|
||||||
<PersonListing person={admin.person} />
|
<PersonListing person={admin.person} />
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
@ -157,7 +158,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={linkEvent(this, this.handleLeaveAdminTeam)}
|
onClick={linkEvent(this, this.handleLeaveAdminTeam)}
|
||||||
class="btn btn-danger mb-2"
|
className="btn btn-danger mb-2"
|
||||||
>
|
>
|
||||||
{this.state.leaveAdminTeamLoading ? (
|
{this.state.leaveAdminTeamLoading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
|
@ -172,9 +173,9 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h5>{i18n.t("banned_users")}</h5>
|
<h5>{i18n.t("banned_users")}</h5>
|
||||||
<ul class="list-unstyled">
|
<ul className="list-unstyled">
|
||||||
{this.state.banned.map(banned => (
|
{this.state.banned.map(banned => (
|
||||||
<li class="list-inline-item">
|
<li key={banned.person.id} className="list-inline-item">
|
||||||
<PersonListing person={banned.person} />
|
<PersonListing person={banned.person} />
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
@ -184,11 +185,10 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLeaveAdminTeam(i: AdminSettings) {
|
handleLeaveAdminTeam(i: AdminSettings) {
|
||||||
i.state.leaveAdminTeamLoading = true;
|
i.setState({ leaveAdminTeamLoading: true });
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.leaveAdmin({ auth: auth().unwrap() })
|
wsClient.leaveAdmin({ auth: auth().unwrap() })
|
||||||
);
|
);
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -197,26 +197,21 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
this.context.router.history.push("/");
|
this.context.router.history.push("/");
|
||||||
this.state.loading = false;
|
this.setState({ loading: false });
|
||||||
this.setState(this.state);
|
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.EditSite) {
|
} else if (op == UserOperation.EditSite) {
|
||||||
let data = wsJsonToRes<SiteResponse>(msg, SiteResponse);
|
let data = wsJsonToRes<SiteResponse>(msg, SiteResponse);
|
||||||
this.state.siteRes.site_view = Some(data.site_view);
|
this.setState(s => ((s.siteRes.site_view = Some(data.site_view)), s));
|
||||||
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, BannedPersonsResponse);
|
let data = wsJsonToRes<BannedPersonsResponse>(msg, BannedPersonsResponse);
|
||||||
this.state.banned = data.banned;
|
this.setState({ banned: data.banned, loading: false });
|
||||||
this.state.loading = false;
|
|
||||||
this.setState(this.state);
|
|
||||||
} else if (op == UserOperation.LeaveAdmin) {
|
} else if (op == UserOperation.LeaveAdmin) {
|
||||||
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
||||||
this.state.siteRes.site_view = data.site_view;
|
this.setState(s => ((s.siteRes.site_view = data.site_view), s));
|
||||||
this.setState(this.state);
|
this.setState({ leaveAdminTeamLoading: false });
|
||||||
this.state.leaveAdminTeamLoading = false;
|
|
||||||
toast(i18n.t("left_admin_team"));
|
toast(i18n.t("left_admin_team"));
|
||||||
this.setState(this.state);
|
|
||||||
this.context.router.history.push("/");
|
this.context.router.history.push("/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import {
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
auth,
|
||||||
|
canCreateCommunity,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
createCommentLikeRes,
|
createCommentLikeRes,
|
||||||
createPostLikeFindRes,
|
createPostLikeFindRes,
|
||||||
|
@ -51,7 +52,9 @@ import {
|
||||||
getPageFromProps,
|
getPageFromProps,
|
||||||
getSortTypeFromProps,
|
getSortTypeFromProps,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
|
isPostBlocked,
|
||||||
notifyPost,
|
notifyPost,
|
||||||
|
nsfwCheck,
|
||||||
postToCommentSortType,
|
postToCommentSortType,
|
||||||
relTags,
|
relTags,
|
||||||
restoreScrollPosition,
|
restoreScrollPosition,
|
||||||
|
@ -157,22 +160,24 @@ export class Home extends Component<any, HomeState> {
|
||||||
let commentsRes = Some(this.isoData.routeData[1] as GetCommentsResponse);
|
let commentsRes = Some(this.isoData.routeData[1] as GetCommentsResponse);
|
||||||
let trendingRes = this.isoData.routeData[2] as ListCommunitiesResponse;
|
let trendingRes = this.isoData.routeData[2] as ListCommunitiesResponse;
|
||||||
|
|
||||||
postsRes.match({
|
if (postsRes.isSome()) {
|
||||||
some: pvs => (this.state.posts = pvs.posts),
|
this.state = { ...this.state, posts: postsRes.unwrap().posts };
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
commentsRes.match({
|
if (commentsRes.isSome()) {
|
||||||
some: cvs => (this.state.comments = cvs.comments),
|
this.state = { ...this.state, comments: commentsRes.unwrap().comments };
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
this.state.trendingCommunities = trendingRes.communities;
|
|
||||||
|
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.communityJoin({ community_id: 0 })
|
wsClient.communityJoin({ community_id: 0 })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.state.loading = false;
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
trendingCommunities: trendingRes.communities,
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this.fetchTrendingCommunities();
|
this.fetchTrendingCommunities();
|
||||||
this.fetchData();
|
this.fetchData();
|
||||||
|
@ -317,7 +322,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
|
@ -325,12 +330,14 @@ export class Home extends Component<any, HomeState> {
|
||||||
image={None}
|
image={None}
|
||||||
/>
|
/>
|
||||||
{this.state.siteRes.site_view.isSome() && (
|
{this.state.siteRes.site_view.isSome() && (
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<main role="main" class="col-12 col-md-8">
|
<main role="main" className="col-12 col-md-8">
|
||||||
<div class="d-block d-md-none">{this.mobileView()}</div>
|
<div className="d-block d-md-none">{this.mobileView()}</div>
|
||||||
{this.posts()}
|
{this.posts()}
|
||||||
</main>
|
</main>
|
||||||
<aside class="d-none d-md-block col-md-4">{this.mySidebar()}</aside>
|
<aside className="d-none d-md-block col-md-4">
|
||||||
|
{this.mySidebar()}
|
||||||
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -347,11 +354,11 @@ export class Home extends Component<any, HomeState> {
|
||||||
mobileView() {
|
mobileView() {
|
||||||
let siteRes = this.state.siteRes;
|
let siteRes = this.state.siteRes;
|
||||||
return (
|
return (
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
{this.hasFollows && (
|
{this.hasFollows && (
|
||||||
<button
|
<button
|
||||||
class="btn btn-secondary d-inline-block mb-2 mr-3"
|
className="btn btn-secondary d-inline-block mb-2 mr-3"
|
||||||
onClick={linkEvent(this, this.handleShowSubscribedMobile)}
|
onClick={linkEvent(this, this.handleShowSubscribedMobile)}
|
||||||
>
|
>
|
||||||
{i18n.t("subscribed")}{" "}
|
{i18n.t("subscribed")}{" "}
|
||||||
|
@ -366,7 +373,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
class="btn btn-secondary d-inline-block mb-2 mr-3"
|
className="btn btn-secondary d-inline-block mb-2 mr-3"
|
||||||
onClick={linkEvent(this, this.handleShowTrendingMobile)}
|
onClick={linkEvent(this, this.handleShowTrendingMobile)}
|
||||||
>
|
>
|
||||||
{i18n.t("trending")}{" "}
|
{i18n.t("trending")}{" "}
|
||||||
|
@ -378,7 +385,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-secondary d-inline-block mb-2 mr-3"
|
className="btn btn-secondary d-inline-block mb-2 mr-3"
|
||||||
onClick={linkEvent(this, this.handleShowSidebarMobile)}
|
onClick={linkEvent(this, this.handleShowSidebarMobile)}
|
||||||
>
|
>
|
||||||
{i18n.t("sidebar")}{" "}
|
{i18n.t("sidebar")}{" "}
|
||||||
|
@ -403,13 +410,13 @@ export class Home extends Component<any, HomeState> {
|
||||||
none: <></>,
|
none: <></>,
|
||||||
})}
|
})}
|
||||||
{this.state.showTrendingMobile && (
|
{this.state.showTrendingMobile && (
|
||||||
<div class="col-12 card border-secondary mb-3">
|
<div className="col-12 card border-secondary mb-3">
|
||||||
<div class="card-body">{this.trendingCommunities()}</div>
|
<div className="card-body">{this.trendingCommunities()}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{this.state.showSubscribedMobile && (
|
{this.state.showSubscribedMobile && (
|
||||||
<div class="col-12 card border-secondary mb-3">
|
<div className="col-12 card border-secondary mb-3">
|
||||||
<div class="card-body">{this.subscribedCommunities()}</div>
|
<div className="card-body">{this.subscribedCommunities()}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -423,10 +430,11 @@ export class Home extends Component<any, HomeState> {
|
||||||
<div>
|
<div>
|
||||||
{!this.state.loading && (
|
{!this.state.loading && (
|
||||||
<div>
|
<div>
|
||||||
<div class="card border-secondary mb-3">
|
<div className="card border-secondary mb-3">
|
||||||
<div class="card-body">
|
<div className="card-body">
|
||||||
{this.trendingCommunities()}
|
{this.trendingCommunities()}
|
||||||
{this.createCommunityButton()}
|
{canCreateCommunity(this.state.siteRes) &&
|
||||||
|
this.createCommunityButton()}
|
||||||
{this.exploreCommunitiesButton()}
|
{this.exploreCommunitiesButton()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -443,8 +451,8 @@ export class Home extends Component<any, HomeState> {
|
||||||
none: <></>,
|
none: <></>,
|
||||||
})}
|
})}
|
||||||
{this.hasFollows && (
|
{this.hasFollows && (
|
||||||
<div class="card border-secondary mb-3">
|
<div className="card border-secondary mb-3">
|
||||||
<div class="card-body">{this.subscribedCommunities()}</div>
|
<div className="card-body">{this.subscribedCommunities()}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -480,9 +488,12 @@ export class Home extends Component<any, HomeState> {
|
||||||
</Link>
|
</Link>
|
||||||
</T>
|
</T>
|
||||||
</h5>
|
</h5>
|
||||||
<ul class="list-inline mb-0">
|
<ul className="list-inline mb-0">
|
||||||
{this.state.trendingCommunities.map(cv => (
|
{this.state.trendingCommunities.map(cv => (
|
||||||
<li class="list-inline-item d-inline-block">
|
<li
|
||||||
|
key={cv.community.id}
|
||||||
|
className="list-inline-item d-inline-block"
|
||||||
|
>
|
||||||
<CommunityLink community={cv.community} />
|
<CommunityLink community={cv.community} />
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
@ -502,7 +513,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
</Link>
|
</Link>
|
||||||
</T>
|
</T>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm text-muted"
|
className="btn btn-sm text-muted"
|
||||||
onClick={linkEvent(this, this.handleCollapseSubscribe)}
|
onClick={linkEvent(this, this.handleCollapseSubscribe)}
|
||||||
aria-label={i18n.t("collapse")}
|
aria-label={i18n.t("collapse")}
|
||||||
data-tippy-content={i18n.t("collapse")}
|
data-tippy-content={i18n.t("collapse")}
|
||||||
|
@ -515,12 +526,15 @@ export class Home extends Component<any, HomeState> {
|
||||||
</button>
|
</button>
|
||||||
</h5>
|
</h5>
|
||||||
{!this.state.subscribedCollapsed && (
|
{!this.state.subscribedCollapsed && (
|
||||||
<ul class="list-inline mb-0">
|
<ul className="list-inline mb-0">
|
||||||
{UserService.Instance.myUserInfo
|
{UserService.Instance.myUserInfo
|
||||||
.map(m => m.follows)
|
.map(m => m.follows)
|
||||||
.unwrapOr([])
|
.unwrapOr([])
|
||||||
.map(cfv => (
|
.map(cfv => (
|
||||||
<li class="list-inline-item d-inline-block">
|
<li
|
||||||
|
key={cfv.community.id}
|
||||||
|
className="list-inline-item d-inline-block"
|
||||||
|
>
|
||||||
<CommunityLink community={cfv.community} />
|
<CommunityLink community={cfv.community} />
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
@ -542,7 +556,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
|
|
||||||
posts() {
|
posts() {
|
||||||
return (
|
return (
|
||||||
<div class="main-content-wrapper">
|
<div className="main-content-wrapper">
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<h5>
|
<h5>
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
|
@ -569,6 +583,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
removeDuplicates
|
removeDuplicates
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<CommentNodes
|
<CommentNodes
|
||||||
|
@ -581,6 +596,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -594,13 +610,13 @@ export class Home extends Component<any, HomeState> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<span class="mr-3">
|
<span className="mr-3">
|
||||||
<DataTypeSelect
|
<DataTypeSelect
|
||||||
type_={this.state.dataType}
|
type_={this.state.dataType}
|
||||||
onChange={this.handleDataTypeChange}
|
onChange={this.handleDataTypeChange}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="mr-3">
|
<span className="mr-3">
|
||||||
<ListingTypeSelect
|
<ListingTypeSelect
|
||||||
type_={this.state.listingType}
|
type_={this.state.listingType}
|
||||||
showLocal={showLocal(this.isoData)}
|
showLocal={showLocal(this.isoData)}
|
||||||
|
@ -608,7 +624,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
onChange={this.handleListingTypeChange}
|
onChange={this.handleListingTypeChange}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="mr-2">
|
<span className="mr-2">
|
||||||
<SortSelect sort={this.state.sort} onChange={this.handleSortChange} />
|
<SortSelect sort={this.state.sort} onChange={this.handleSortChange} />
|
||||||
</span>
|
</span>
|
||||||
{this.state.listingType == ListingType.All && (
|
{this.state.listingType == ListingType.All && (
|
||||||
|
@ -644,23 +660,19 @@ export class Home extends Component<any, HomeState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowSubscribedMobile(i: Home) {
|
handleShowSubscribedMobile(i: Home) {
|
||||||
i.state.showSubscribedMobile = !i.state.showSubscribedMobile;
|
i.setState({ showSubscribedMobile: !i.state.showSubscribedMobile });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowTrendingMobile(i: Home) {
|
handleShowTrendingMobile(i: Home) {
|
||||||
i.state.showTrendingMobile = !i.state.showTrendingMobile;
|
i.setState({ showTrendingMobile: !i.state.showTrendingMobile });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowSidebarMobile(i: Home) {
|
handleShowSidebarMobile(i: Home) {
|
||||||
i.state.showSidebarMobile = !i.state.showSidebarMobile;
|
i.setState({ showSidebarMobile: !i.state.showSidebarMobile });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCollapseSubscribe(i: Home) {
|
handleCollapseSubscribe(i: Home) {
|
||||||
i.state.subscribedCollapsed = !i.state.subscribedCollapsed;
|
i.setState({ subscribedCollapsed: !i.state.subscribedCollapsed });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePageChange(page: number) {
|
handlePageChange(page: number) {
|
||||||
|
@ -731,18 +743,14 @@ export class Home extends Component<any, HomeState> {
|
||||||
msg,
|
msg,
|
||||||
ListCommunitiesResponse
|
ListCommunitiesResponse
|
||||||
);
|
);
|
||||||
this.state.trendingCommunities = data.communities;
|
this.setState({ trendingCommunities: data.communities });
|
||||||
this.setState(this.state);
|
|
||||||
} else if (op == UserOperation.EditSite) {
|
} else if (op == UserOperation.EditSite) {
|
||||||
let data = wsJsonToRes<SiteResponse>(msg, SiteResponse);
|
let data = wsJsonToRes<SiteResponse>(msg, SiteResponse);
|
||||||
this.state.siteRes.site_view = Some(data.site_view);
|
this.setState(s => ((s.siteRes.site_view = Some(data.site_view)), s));
|
||||||
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, GetPostsResponse);
|
let data = wsJsonToRes<GetPostsResponse>(msg, GetPostsResponse);
|
||||||
this.state.posts = data.posts;
|
this.setState({ posts: data.posts, loading: false });
|
||||||
this.state.loading = false;
|
|
||||||
this.setState(this.state);
|
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.communityJoin({ community_id: 0 })
|
wsClient.communityJoin({ community_id: 0 })
|
||||||
);
|
);
|
||||||
|
@ -750,21 +758,17 @@ export class Home extends Component<any, HomeState> {
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.CreatePost) {
|
} else if (op == UserOperation.CreatePost) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
||||||
// NSFW check
|
|
||||||
let nsfw = data.post_view.post.nsfw || data.post_view.community.nsfw;
|
|
||||||
let nsfwCheck =
|
|
||||||
!nsfw ||
|
|
||||||
(nsfw &&
|
|
||||||
UserService.Instance.myUserInfo
|
|
||||||
.map(m => m.local_user_view.local_user.show_nsfw)
|
|
||||||
.unwrapOr(false));
|
|
||||||
|
|
||||||
let showPostNotifs = UserService.Instance.myUserInfo
|
let showPostNotifs = UserService.Instance.myUserInfo
|
||||||
.map(m => m.local_user_view.local_user.show_new_post_notifs)
|
.map(m => m.local_user_view.local_user.show_new_post_notifs)
|
||||||
.unwrapOr(false);
|
.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, you pass the nsfw check, and it isn't blocked
|
||||||
if (this.state.page == 1 && nsfwCheck) {
|
if (
|
||||||
|
this.state.page == 1 &&
|
||||||
|
nsfwCheck(data.post_view) &&
|
||||||
|
!isPostBlocked(data.post_view)
|
||||||
|
) {
|
||||||
// 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 (
|
||||||
|
@ -812,8 +816,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.AddAdmin) {
|
} else if (op == UserOperation.AddAdmin) {
|
||||||
let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
|
let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
|
||||||
this.state.siteRes.admins = data.admins;
|
this.setState(s => ((s.siteRes.admins = data.admins), s));
|
||||||
this.setState(this.state);
|
|
||||||
} else if (op == UserOperation.BanPerson) {
|
} else if (op == UserOperation.BanPerson) {
|
||||||
let data = wsJsonToRes<BanPersonResponse>(msg, BanPersonResponse);
|
let data = wsJsonToRes<BanPersonResponse>(msg, BanPersonResponse);
|
||||||
this.state.posts
|
this.state.posts
|
||||||
|
@ -823,9 +826,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.GetComments) {
|
} else if (op == UserOperation.GetComments) {
|
||||||
let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
|
let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
|
||||||
this.state.comments = data.comments;
|
this.setState({ comments: data.comments, loading: false });
|
||||||
this.state.loading = false;
|
|
||||||
this.setState(this.state);
|
|
||||||
} else if (
|
} else if (
|
||||||
op == UserOperation.EditComment ||
|
op == UserOperation.EditComment ||
|
||||||
op == UserOperation.DeleteComment ||
|
op == UserOperation.DeleteComment ||
|
||||||
|
|
|
@ -30,22 +30,22 @@ export class Instances extends Component<any, InstancesState> {
|
||||||
render() {
|
render() {
|
||||||
return this.state.siteRes.federated_instances.match({
|
return this.state.siteRes.federated_instances.match({
|
||||||
some: federated_instances => (
|
some: federated_instances => (
|
||||||
<div class="container">
|
<div className="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}
|
description={None}
|
||||||
image={None}
|
image={None}
|
||||||
/>
|
/>
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-md-6">
|
<div className="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.match({
|
{federated_instances.allowed.match({
|
||||||
some: allowed =>
|
some: allowed =>
|
||||||
allowed.length > 0 && (
|
allowed.length > 0 && (
|
||||||
<div class="col-md-6">
|
<div className="col-md-6">
|
||||||
<h5>{i18n.t("allowed_instances")}</h5>
|
<h5>{i18n.t("allowed_instances")}</h5>
|
||||||
{this.itemList(allowed)}
|
{this.itemList(allowed)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,7 +55,7 @@ export class Instances extends Component<any, InstancesState> {
|
||||||
{federated_instances.blocked.match({
|
{federated_instances.blocked.match({
|
||||||
some: blocked =>
|
some: blocked =>
|
||||||
blocked.length > 0 && (
|
blocked.length > 0 && (
|
||||||
<div class="col-md-6">
|
<div className="col-md-6">
|
||||||
<h5>{i18n.t("blocked_instances")}</h5>
|
<h5>{i18n.t("blocked_instances")}</h5>
|
||||||
{this.itemList(blocked)}
|
{this.itemList(blocked)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -74,7 +74,7 @@ export class Instances extends Component<any, InstancesState> {
|
||||||
return items.length > 0 ? (
|
return items.length > 0 ? (
|
||||||
<ul>
|
<ul>
|
||||||
{items.map(i => (
|
{items.map(i => (
|
||||||
<li>
|
<li key={i}>
|
||||||
<a href={`https://${i}`} rel={relTags}>
|
<a href={`https://${i}`} rel={relTags}>
|
||||||
{i}
|
{i}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -26,7 +26,7 @@ export class Legal extends Component<any, LegalState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
|
|
|
@ -81,15 +81,15 @@ export class Login extends Component<any, State> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="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}
|
description={None}
|
||||||
image={None}
|
image={None}
|
||||||
/>
|
/>
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12 col-lg-6 offset-lg-3">{this.loginForm()}</div>
|
<div className="col-12 col-lg-6 offset-lg-3">{this.loginForm()}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -100,17 +100,17 @@ export class Login extends Component<any, State> {
|
||||||
<div>
|
<div>
|
||||||
<form onSubmit={linkEvent(this, this.handleLoginSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleLoginSubmit)}>
|
||||||
<h5>{i18n.t("login")}</h5>
|
<h5>{i18n.t("login")}</h5>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label
|
<label
|
||||||
class="col-sm-2 col-form-label"
|
className="col-sm-2 col-form-label"
|
||||||
htmlFor="login-email-or-username"
|
htmlFor="login-email-or-username"
|
||||||
>
|
>
|
||||||
{i18n.t("email_or_username")}
|
{i18n.t("email_or_username")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
id="login-email-or-username"
|
id="login-email-or-username"
|
||||||
value={this.state.loginForm.username_or_email}
|
value={this.state.loginForm.username_or_email}
|
||||||
onInput={linkEvent(this, this.handleLoginUsernameChange)}
|
onInput={linkEvent(this, this.handleLoginUsernameChange)}
|
||||||
|
@ -120,17 +120,17 @@ export class Login extends Component<any, State> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="login-password">
|
<label className="col-sm-2 col-form-label" htmlFor="login-password">
|
||||||
{i18n.t("password")}
|
{i18n.t("password")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="login-password"
|
id="login-password"
|
||||||
value={this.state.loginForm.password}
|
value={this.state.loginForm.password}
|
||||||
onInput={linkEvent(this, this.handleLoginPasswordChange)}
|
onInput={linkEvent(this, this.handleLoginPasswordChange)}
|
||||||
class="form-control"
|
className="form-control"
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
required
|
required
|
||||||
maxLength={60}
|
maxLength={60}
|
||||||
|
@ -146,9 +146,9 @@ export class Login extends Component<any, State> {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<button type="submit" class="btn btn-secondary">
|
<button type="submit" className="btn btn-secondary">
|
||||||
{this.state.loginLoading ? <Spinner /> : i18n.t("login")}
|
{this.state.loginLoading ? <Spinner /> : i18n.t("login")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -160,8 +160,7 @@ export class Login extends Component<any, State> {
|
||||||
|
|
||||||
handleLoginSubmit(i: Login, event: any) {
|
handleLoginSubmit(i: Login, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.loginLoading = true;
|
i.setState({ loginLoading: true });
|
||||||
i.setState(i.state);
|
|
||||||
WebSocketService.Instance.send(wsClient.login(i.state.loginForm));
|
WebSocketService.Instance.send(wsClient.login(i.state.loginForm));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,21 +187,18 @@ export class Login extends Component<any, State> {
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
this.state = this.emptyState;
|
this.setState(this.emptyState);
|
||||||
this.setState(this.state);
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (op == UserOperation.Login) {
|
if (op == UserOperation.Login) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
||||||
this.state = this.emptyState;
|
this.setState(this.emptyState);
|
||||||
this.setState(this.state);
|
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
} else if (op == UserOperation.PasswordReset) {
|
} else if (op == UserOperation.PasswordReset) {
|
||||||
toast(i18n.t("reset_password_mail_sent"));
|
toast(i18n.t("reset_password_mail_sent"));
|
||||||
} else if (op == UserOperation.GetSite) {
|
} else if (op == UserOperation.GetSite) {
|
||||||
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
||||||
this.state.siteRes = data;
|
this.setState({ siteRes: data });
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,10 +67,10 @@ export class Setup extends Component<any, State> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
<Helmet title={this.documentTitle} />
|
<Helmet title={this.documentTitle} />
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12 offset-lg-3 col-lg-6">
|
<div className="col-12 offset-lg-3 col-lg-6">
|
||||||
<h3>{i18n.t("lemmy_instance_setup")}</h3>
|
<h3>{i18n.t("lemmy_instance_setup")}</h3>
|
||||||
{!this.state.doneRegisteringUser ? (
|
{!this.state.doneRegisteringUser ? (
|
||||||
this.registerUser()
|
this.registerUser()
|
||||||
|
@ -87,14 +87,14 @@ export class Setup extends Component<any, State> {
|
||||||
return (
|
return (
|
||||||
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
|
||||||
<h5>{i18n.t("setup_admin")}</h5>
|
<h5>{i18n.t("setup_admin")}</h5>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="username">
|
<label className="col-sm-2 col-form-label" htmlFor="username">
|
||||||
{i18n.t("username")}
|
{i18n.t("username")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
id="username"
|
id="username"
|
||||||
value={this.state.userForm.username}
|
value={this.state.userForm.username}
|
||||||
onInput={linkEvent(this, this.handleRegisterUsernameChange)}
|
onInput={linkEvent(this, this.handleRegisterUsernameChange)}
|
||||||
|
@ -104,16 +104,16 @@ export class Setup extends Component<any, State> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="email">
|
<label className="col-sm-2 col-form-label" htmlFor="email">
|
||||||
{i18n.t("email")}
|
{i18n.t("email")}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
id="email"
|
id="email"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
placeholder={i18n.t("optional")}
|
placeholder={i18n.t("optional")}
|
||||||
value={toUndefined(this.state.userForm.email)}
|
value={toUndefined(this.state.userForm.email)}
|
||||||
onInput={linkEvent(this, this.handleRegisterEmailChange)}
|
onInput={linkEvent(this, this.handleRegisterEmailChange)}
|
||||||
|
@ -121,17 +121,17 @@ export class Setup extends Component<any, State> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="password">
|
<label className="col-sm-2 col-form-label" htmlFor="password">
|
||||||
{i18n.t("password")}
|
{i18n.t("password")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="password"
|
id="password"
|
||||||
value={this.state.userForm.password}
|
value={this.state.userForm.password}
|
||||||
onInput={linkEvent(this, this.handleRegisterPasswordChange)}
|
onInput={linkEvent(this, this.handleRegisterPasswordChange)}
|
||||||
class="form-control"
|
className="form-control"
|
||||||
required
|
required
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
minLength={10}
|
minLength={10}
|
||||||
|
@ -139,17 +139,17 @@ export class Setup extends Component<any, State> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="verify-password">
|
<label className="col-sm-2 col-form-label" htmlFor="verify-password">
|
||||||
{i18n.t("verify_password")}
|
{i18n.t("verify_password")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="verify-password"
|
id="verify-password"
|
||||||
value={this.state.userForm.password_verify}
|
value={this.state.userForm.password_verify}
|
||||||
onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
|
onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
|
||||||
class="form-control"
|
className="form-control"
|
||||||
required
|
required
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
minLength={10}
|
minLength={10}
|
||||||
|
@ -157,9 +157,9 @@ export class Setup extends Component<any, State> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<button type="submit" class="btn btn-secondary">
|
<button type="submit" className="btn btn-secondary">
|
||||||
{this.state.userLoading ? <Spinner /> : i18n.t("sign_up")}
|
{this.state.userLoading ? <Spinner /> : i18n.t("sign_up")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -170,8 +170,7 @@ export class Setup extends Component<any, State> {
|
||||||
|
|
||||||
handleRegisterSubmit(i: Setup, event: any) {
|
handleRegisterSubmit(i: Setup, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.userLoading = true;
|
i.setState({ userLoading: true });
|
||||||
i.setState(i.state);
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
WebSocketService.Instance.send(wsClient.register(i.state.userForm));
|
WebSocketService.Instance.send(wsClient.register(i.state.userForm));
|
||||||
}
|
}
|
||||||
|
@ -200,14 +199,12 @@ export class Setup extends Component<any, State> {
|
||||||
let op = wsUserOp(msg);
|
let op = wsUserOp(msg);
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
this.state.userLoading = false;
|
this.setState({ userLoading: false });
|
||||||
this.setState(this.state);
|
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.Register) {
|
} else if (op == UserOperation.Register) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
||||||
this.state.userLoading = false;
|
this.setState({ userLoading: false });
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
this.setState(this.state);
|
|
||||||
} else if (op == UserOperation.CreateSite) {
|
} else if (op == UserOperation.CreateSite) {
|
||||||
window.location.href = "/";
|
window.location.href = "/";
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,15 +127,17 @@ export class Signup extends Component<any, State> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="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}
|
description={None}
|
||||||
image={None}
|
image={None}
|
||||||
/>
|
/>
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12 col-lg-6 offset-lg-3">{this.registerForm()}</div>
|
<div className="col-12 col-lg-6 offset-lg-3">
|
||||||
|
{this.registerForm()}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -148,8 +150,8 @@ export class Signup extends Component<any, State> {
|
||||||
<h5>{this.titleName(siteView)}</h5>
|
<h5>{this.titleName(siteView)}</h5>
|
||||||
|
|
||||||
{this.isLemmyMl && (
|
{this.isLemmyMl && (
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="mt-2 mb-0 alert alert-warning" role="alert">
|
<div className="mt-2 mb-0 alert alert-warning" role="alert">
|
||||||
<T i18nKey="lemmy_ml_registration_message">
|
<T i18nKey="lemmy_ml_registration_message">
|
||||||
#<a href={joinLemmyUrl}>#</a>
|
#<a href={joinLemmyUrl}>#</a>
|
||||||
</T>
|
</T>
|
||||||
|
@ -157,16 +159,19 @@ export class Signup extends Component<any, State> {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="register-username">
|
<label
|
||||||
|
className="col-sm-2 col-form-label"
|
||||||
|
htmlFor="register-username"
|
||||||
|
>
|
||||||
{i18n.t("username")}
|
{i18n.t("username")}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="register-username"
|
id="register-username"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
value={this.state.registerForm.username}
|
value={this.state.registerForm.username}
|
||||||
onInput={linkEvent(this, this.handleRegisterUsernameChange)}
|
onInput={linkEvent(this, this.handleRegisterUsernameChange)}
|
||||||
required
|
required
|
||||||
|
@ -177,15 +182,15 @@ export class Signup extends Component<any, State> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="register-email">
|
<label className="col-sm-2 col-form-label" htmlFor="register-email">
|
||||||
{i18n.t("email")}
|
{i18n.t("email")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
id="register-email"
|
id="register-email"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
placeholder={
|
placeholder={
|
||||||
siteView.site.require_email_verification
|
siteView.site.require_email_verification
|
||||||
? i18n.t("required")
|
? i18n.t("required")
|
||||||
|
@ -201,7 +206,7 @@ export class Signup extends Component<any, State> {
|
||||||
!this.state.registerForm.email
|
!this.state.registerForm.email
|
||||||
.map(validEmail)
|
.map(validEmail)
|
||||||
.unwrapOr(true) && (
|
.unwrapOr(true) && (
|
||||||
<div class="mt-2 mb-0 alert alert-warning" role="alert">
|
<div className="mt-2 mb-0 alert alert-warning" role="alert">
|
||||||
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
||||||
{i18n.t("no_password_reset")}
|
{i18n.t("no_password_reset")}
|
||||||
</div>
|
</div>
|
||||||
|
@ -209,11 +214,14 @@ export class Signup extends Component<any, State> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="register-password">
|
<label
|
||||||
|
className="col-sm-2 col-form-label"
|
||||||
|
htmlFor="register-password"
|
||||||
|
>
|
||||||
{i18n.t("password")}
|
{i18n.t("password")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="register-password"
|
id="register-password"
|
||||||
|
@ -222,25 +230,25 @@ export class Signup extends Component<any, State> {
|
||||||
onInput={linkEvent(this, this.handleRegisterPasswordChange)}
|
onInput={linkEvent(this, this.handleRegisterPasswordChange)}
|
||||||
minLength={10}
|
minLength={10}
|
||||||
maxLength={60}
|
maxLength={60}
|
||||||
class="form-control"
|
className="form-control"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
{this.state.registerForm.password && (
|
{this.state.registerForm.password && (
|
||||||
<div class={this.passwordColorClass}>
|
<div className={this.passwordColorClass}>
|
||||||
{i18n.t(this.passwordStrength as I18nKeys)}
|
{i18n.t(this.passwordStrength as I18nKeys)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label
|
<label
|
||||||
class="col-sm-2 col-form-label"
|
className="col-sm-2 col-form-label"
|
||||||
htmlFor="register-verify-password"
|
htmlFor="register-verify-password"
|
||||||
>
|
>
|
||||||
{i18n.t("verify_password")}
|
{i18n.t("verify_password")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="register-verify-password"
|
id="register-verify-password"
|
||||||
|
@ -251,7 +259,7 @@ export class Signup extends Component<any, State> {
|
||||||
this.handleRegisterPasswordVerifyChange
|
this.handleRegisterPasswordVerifyChange
|
||||||
)}
|
)}
|
||||||
maxLength={60}
|
maxLength={60}
|
||||||
class="form-control"
|
className="form-control"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -259,9 +267,9 @@ export class Signup extends Component<any, State> {
|
||||||
|
|
||||||
{siteView.site.require_application && (
|
{siteView.site.require_application && (
|
||||||
<>
|
<>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="offset-sm-2 col-sm-10">
|
<div className="offset-sm-2 col-sm-10">
|
||||||
<div class="mt-2 alert alert-warning" role="alert">
|
<div className="mt-2 alert alert-warning" role="alert">
|
||||||
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
||||||
{i18n.t("fill_out_application")}
|
{i18n.t("fill_out_application")}
|
||||||
</div>
|
</div>
|
||||||
|
@ -277,21 +285,23 @@ export class Signup extends Component<any, State> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label
|
<label
|
||||||
class="col-sm-2 col-form-label"
|
className="col-sm-2 col-form-label"
|
||||||
htmlFor="application_answer"
|
htmlFor="application_answer"
|
||||||
>
|
>
|
||||||
{i18n.t("answer")}
|
{i18n.t("answer")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={None}
|
initialContent={None}
|
||||||
|
initialLanguageId={None}
|
||||||
placeholder={None}
|
placeholder={None}
|
||||||
buttonTitle={None}
|
buttonTitle={None}
|
||||||
maxLength={None}
|
maxLength={None}
|
||||||
onContentChange={this.handleAnswerChange}
|
onContentChange={this.handleAnswerChange}
|
||||||
hideNavigationWarnings
|
hideNavigationWarnings
|
||||||
|
allLanguages={[]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -299,12 +309,12 @@ export class Signup extends Component<any, State> {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{this.state.captcha.isSome() && (
|
{this.state.captcha.isSome() && (
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2" htmlFor="register-captcha">
|
<label className="col-sm-2" htmlFor="register-captcha">
|
||||||
<span class="mr-2">{i18n.t("enter_code")}</span>
|
<span className="mr-2">{i18n.t("enter_code")}</span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
onClick={linkEvent(this, this.handleRegenCaptcha)}
|
onClick={linkEvent(this, this.handleRegenCaptcha)}
|
||||||
aria-label={i18n.t("captcha")}
|
aria-label={i18n.t("captcha")}
|
||||||
>
|
>
|
||||||
|
@ -312,10 +322,10 @@ export class Signup extends Component<any, State> {
|
||||||
</button>
|
</button>
|
||||||
</label>
|
</label>
|
||||||
{this.showCaptcha()}
|
{this.showCaptcha()}
|
||||||
<div class="col-sm-6">
|
<div className="col-sm-6">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
id="register-captcha"
|
id="register-captcha"
|
||||||
value={toUndefined(this.state.registerForm.captcha_answer)}
|
value={toUndefined(this.state.registerForm.captcha_answer)}
|
||||||
onInput={linkEvent(
|
onInput={linkEvent(
|
||||||
|
@ -328,11 +338,11 @@ export class Signup extends Component<any, State> {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{siteView.site.enable_nsfw && (
|
{siteView.site.enable_nsfw && (
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="register-show-nsfw"
|
id="register-show-nsfw"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.registerForm.show_nsfw}
|
checked={this.state.registerForm.show_nsfw}
|
||||||
|
@ -341,7 +351,10 @@ export class Signup extends Component<any, State> {
|
||||||
this.handleRegisterShowNsfwChange
|
this.handleRegisterShowNsfwChange
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" htmlFor="register-show-nsfw">
|
<label
|
||||||
|
className="form-check-label"
|
||||||
|
htmlFor="register-show-nsfw"
|
||||||
|
>
|
||||||
{i18n.t("show_nsfw")}
|
{i18n.t("show_nsfw")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -353,14 +366,14 @@ export class Signup extends Component<any, State> {
|
||||||
autoComplete="false"
|
autoComplete="false"
|
||||||
name="a_password"
|
name="a_password"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control honeypot"
|
className="form-control honeypot"
|
||||||
id="register-honey"
|
id="register-honey"
|
||||||
value={toUndefined(this.state.registerForm.honeypot)}
|
value={toUndefined(this.state.registerForm.honeypot)}
|
||||||
onInput={linkEvent(this, this.handleHoneyPotChange)}
|
onInput={linkEvent(this, this.handleHoneyPotChange)}
|
||||||
/>
|
/>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<button type="submit" class="btn btn-secondary">
|
<button type="submit" className="btn btn-secondary">
|
||||||
{this.state.registerLoading ? (
|
{this.state.registerLoading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : (
|
) : (
|
||||||
|
@ -378,19 +391,19 @@ export class Signup extends Component<any, State> {
|
||||||
showCaptcha() {
|
showCaptcha() {
|
||||||
return this.state.captcha.match({
|
return this.state.captcha.match({
|
||||||
some: captcha => (
|
some: captcha => (
|
||||||
<div class="col-sm-4">
|
<div className="col-sm-4">
|
||||||
{captcha.ok.match({
|
{captcha.ok.match({
|
||||||
some: res => (
|
some: res => (
|
||||||
<>
|
<>
|
||||||
<img
|
<img
|
||||||
class="rounded-top img-fluid"
|
className="rounded-top img-fluid"
|
||||||
src={this.captchaPngSrc(res)}
|
src={this.captchaPngSrc(res)}
|
||||||
style="border-bottom-right-radius: 0; border-bottom-left-radius: 0;"
|
style="border-bottom-right-radius: 0; border-bottom-left-radius: 0;"
|
||||||
alt={i18n.t("captcha")}
|
alt={i18n.t("captcha")}
|
||||||
/>
|
/>
|
||||||
{res.wav.isSome() && (
|
{res.wav.isSome() && (
|
||||||
<button
|
<button
|
||||||
class="rounded-bottom btn btn-sm btn-secondary btn-block"
|
className="rounded-bottom btn btn-sm btn-secondary btn-block"
|
||||||
style="border-top-right-radius: 0; border-top-left-radius: 0;"
|
style="border-top-right-radius: 0; border-top-left-radius: 0;"
|
||||||
title={i18n.t("play_captcha_audio")}
|
title={i18n.t("play_captcha_audio")}
|
||||||
onClick={linkEvent(this, this.handleCaptchaPlay)}
|
onClick={linkEvent(this, this.handleCaptchaPlay)}
|
||||||
|
@ -431,8 +444,7 @@ export class Signup extends Component<any, State> {
|
||||||
|
|
||||||
handleRegisterSubmit(i: Signup, event: any) {
|
handleRegisterSubmit(i: Signup, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.registerLoading = true;
|
i.setState({ registerLoading: true });
|
||||||
i.setState(i.state);
|
|
||||||
WebSocketService.Instance.send(wsClient.register(i.state.registerForm));
|
WebSocketService.Instance.send(wsClient.register(i.state.registerForm));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,8 +482,7 @@ export class Signup extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAnswerChange(val: string) {
|
handleAnswerChange(val: string) {
|
||||||
this.state.registerForm.answer = Some(val);
|
this.setState(s => ((s.registerForm.answer = Some(val)), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleHoneyPotChange(i: Signup, event: any) {
|
handleHoneyPotChange(i: Signup, event: any) {
|
||||||
|
@ -481,8 +492,7 @@ export class Signup extends Component<any, State> {
|
||||||
|
|
||||||
handleRegenCaptcha(i: Signup) {
|
handleRegenCaptcha(i: Signup) {
|
||||||
i.audio = null;
|
i.audio = null;
|
||||||
i.state.captchaPlaying = false;
|
i.setState({ captchaPlaying: false });
|
||||||
i.setState(i.state);
|
|
||||||
WebSocketService.Instance.send(wsClient.getCaptcha());
|
WebSocketService.Instance.send(wsClient.getCaptcha());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,13 +510,11 @@ export class Signup extends Component<any, State> {
|
||||||
|
|
||||||
i.audio.play();
|
i.audio.play();
|
||||||
|
|
||||||
i.state.captchaPlaying = true;
|
i.setState({ captchaPlaying: true });
|
||||||
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.setState({ captchaPlaying: false });
|
||||||
i.setState(i.state);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
none: void 0,
|
none: void 0,
|
||||||
|
@ -524,17 +532,15 @@ export class Signup extends Component<any, State> {
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
this.state = this.emptyState;
|
this.setState(this.emptyState);
|
||||||
this.state.registerForm.captcha_answer = undefined;
|
this.setState(s => ((s.registerForm.captcha_answer = undefined), s));
|
||||||
// Refetch another captcha
|
// Refetch another captcha
|
||||||
// WebSocketService.Instance.send(wsClient.getCaptcha());
|
// WebSocketService.Instance.send(wsClient.getCaptcha());
|
||||||
this.setState(this.state);
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (op == UserOperation.Register) {
|
if (op == UserOperation.Register) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
||||||
this.state = this.emptyState;
|
this.setState(this.emptyState);
|
||||||
this.setState(this.state);
|
|
||||||
// Only log them in if a jwt was set
|
// Only log them in if a jwt was set
|
||||||
if (data.jwt.isSome()) {
|
if (data.jwt.isSome()) {
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
|
@ -552,9 +558,10 @@ export class Signup extends Component<any, State> {
|
||||||
let data = wsJsonToRes<GetCaptchaResponse>(msg, GetCaptchaResponse);
|
let data = wsJsonToRes<GetCaptchaResponse>(msg, GetCaptchaResponse);
|
||||||
data.ok.match({
|
data.ok.match({
|
||||||
some: res => {
|
some: res => {
|
||||||
this.state.captcha = Some(data);
|
this.setState({ captcha: Some(data) });
|
||||||
this.state.registerForm.captcha_uuid = Some(res.uuid);
|
this.setState(
|
||||||
this.setState(this.state);
|
s => ((s.registerForm.captcha_uuid = Some(res.uuid)), s)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
none: void 0,
|
none: void 0,
|
||||||
});
|
});
|
||||||
|
@ -562,8 +569,7 @@ export class Signup extends Component<any, State> {
|
||||||
toast(i18n.t("reset_password_mail_sent"));
|
toast(i18n.t("reset_password_mail_sent"));
|
||||||
} else if (op == UserOperation.GetSite) {
|
} else if (op == UserOperation.GetSite) {
|
||||||
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
||||||
this.state.siteRes = data;
|
this.setState({ siteRes: data });
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
legal_information: None,
|
legal_information: None,
|
||||||
description: None,
|
description: None,
|
||||||
community_creation_admin_only: None,
|
community_creation_admin_only: None,
|
||||||
|
application_email_admins: None,
|
||||||
auth: undefined,
|
auth: undefined,
|
||||||
hide_modlog_mod_names: Some(true),
|
hide_modlog_mod_names: Some(true),
|
||||||
}),
|
}),
|
||||||
|
@ -78,9 +79,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
this.handleDefaultPostListingTypeChange =
|
this.handleDefaultPostListingTypeChange =
|
||||||
this.handleDefaultPostListingTypeChange.bind(this);
|
this.handleDefaultPostListingTypeChange.bind(this);
|
||||||
|
|
||||||
this.props.site.match({
|
if (this.props.site.isSome()) {
|
||||||
some: site => {
|
let site = this.props.site.unwrap();
|
||||||
this.state.siteForm = new EditSite({
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
siteForm: new EditSite({
|
||||||
name: Some(site.name),
|
name: Some(site.name),
|
||||||
sidebar: site.sidebar,
|
sidebar: site.sidebar,
|
||||||
description: site.description,
|
description: site.description,
|
||||||
|
@ -99,23 +102,21 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
default_theme: Some(site.default_theme),
|
default_theme: Some(site.default_theme),
|
||||||
default_post_listing_type: Some(site.default_post_listing_type),
|
default_post_listing_type: Some(site.default_post_listing_type),
|
||||||
legal_information: site.legal_information,
|
legal_information: site.legal_information,
|
||||||
|
application_email_admins: Some(site.application_email_admins),
|
||||||
hide_modlog_mod_names: site.hide_modlog_mod_names,
|
hide_modlog_mod_names: site.hide_modlog_mod_names,
|
||||||
auth: undefined,
|
auth: undefined,
|
||||||
});
|
}),
|
||||||
},
|
};
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
this.state.themeList = Some(await fetchThemeList());
|
this.setState({ themeList: Some(await fetchThemeList()) });
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Necessary to stop the loading
|
// Necessary to stop the loading
|
||||||
componentWillReceiveProps() {
|
componentWillReceiveProps() {
|
||||||
this.state.loading = false;
|
this.setState({ loading: false });
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
|
@ -157,15 +158,15 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
? 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>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-12 col-form-label" htmlFor="create-site-name">
|
<label className="col-12 col-form-label" htmlFor="create-site-name">
|
||||||
{i18n.t("name")}
|
{i18n.t("name")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="create-site-name"
|
id="create-site-name"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
value={toUndefined(this.state.siteForm.name)}
|
value={toUndefined(this.state.siteForm.name)}
|
||||||
onInput={linkEvent(this, this.handleSiteNameChange)}
|
onInput={linkEvent(this, this.handleSiteNameChange)}
|
||||||
required
|
required
|
||||||
|
@ -174,7 +175,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<label>{i18n.t("icon")}</label>
|
<label>{i18n.t("icon")}</label>
|
||||||
<ImageUploadForm
|
<ImageUploadForm
|
||||||
uploadTitle={i18n.t("upload_icon")}
|
uploadTitle={i18n.t("upload_icon")}
|
||||||
|
@ -184,7 +185,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
rounded
|
rounded
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<label>{i18n.t("banner")}</label>
|
<label>{i18n.t("banner")}</label>
|
||||||
<ImageUploadForm
|
<ImageUploadForm
|
||||||
uploadTitle={i18n.t("upload_banner")}
|
uploadTitle={i18n.t("upload_banner")}
|
||||||
|
@ -193,14 +194,14 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
onRemove={this.handleBannerRemove}
|
onRemove={this.handleBannerRemove}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-12 col-form-label" htmlFor="site-desc">
|
<label className="col-12 col-form-label" htmlFor="site-desc">
|
||||||
{i18n.t("description")}
|
{i18n.t("description")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
id="site-desc"
|
id="site-desc"
|
||||||
value={toUndefined(this.state.siteForm.description)}
|
value={toUndefined(this.state.siteForm.description)}
|
||||||
onInput={linkEvent(this, this.handleSiteDescChange)}
|
onInput={linkEvent(this, this.handleSiteDescChange)}
|
||||||
|
@ -208,56 +209,62 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-12 col-form-label">{i18n.t("sidebar")}</label>
|
<label className="col-12 col-form-label">{i18n.t("sidebar")}</label>
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.siteForm.sidebar}
|
initialContent={this.state.siteForm.sidebar}
|
||||||
|
initialLanguageId={None}
|
||||||
placeholder={None}
|
placeholder={None}
|
||||||
buttonTitle={None}
|
buttonTitle={None}
|
||||||
maxLength={None}
|
maxLength={None}
|
||||||
onContentChange={this.handleSiteSidebarChange}
|
onContentChange={this.handleSiteSidebarChange}
|
||||||
hideNavigationWarnings
|
hideNavigationWarnings
|
||||||
|
allLanguages={[]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-12 col-form-label">
|
<label className="col-12 col-form-label">
|
||||||
{i18n.t("legal_information")}
|
{i18n.t("legal_information")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.siteForm.legal_information}
|
initialContent={this.state.siteForm.legal_information}
|
||||||
|
initialLanguageId={None}
|
||||||
placeholder={None}
|
placeholder={None}
|
||||||
buttonTitle={None}
|
buttonTitle={None}
|
||||||
maxLength={None}
|
maxLength={None}
|
||||||
onContentChange={this.handleSiteLegalInfoChange}
|
onContentChange={this.handleSiteLegalInfoChange}
|
||||||
hideNavigationWarnings
|
hideNavigationWarnings
|
||||||
|
allLanguages={[]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{this.state.siteForm.require_application.unwrapOr(false) && (
|
{this.state.siteForm.require_application.unwrapOr(false) && (
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-12 col-form-label">
|
<label className="col-12 col-form-label">
|
||||||
{i18n.t("application_questionnaire")}
|
{i18n.t("application_questionnaire")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.siteForm.application_question}
|
initialContent={this.state.siteForm.application_question}
|
||||||
|
initialLanguageId={None}
|
||||||
placeholder={None}
|
placeholder={None}
|
||||||
buttonTitle={None}
|
buttonTitle={None}
|
||||||
maxLength={None}
|
maxLength={None}
|
||||||
onContentChange={this.handleSiteApplicationQuestionChange}
|
onContentChange={this.handleSiteApplicationQuestionChange}
|
||||||
hideNavigationWarnings
|
hideNavigationWarnings
|
||||||
|
allLanguages={[]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="create-site-downvotes"
|
id="create-site-downvotes"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(this.state.siteForm.enable_downvotes)}
|
checked={toUndefined(this.state.siteForm.enable_downvotes)}
|
||||||
|
@ -266,24 +273,27 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
this.handleSiteEnableDownvotesChange
|
this.handleSiteEnableDownvotesChange
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" htmlFor="create-site-downvotes">
|
<label
|
||||||
|
className="form-check-label"
|
||||||
|
htmlFor="create-site-downvotes"
|
||||||
|
>
|
||||||
{i18n.t("enable_downvotes")}
|
{i18n.t("enable_downvotes")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="create-site-enable-nsfw"
|
id="create-site-enable-nsfw"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(this.state.siteForm.enable_nsfw)}
|
checked={toUndefined(this.state.siteForm.enable_nsfw)}
|
||||||
onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
|
onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
class="form-check-label"
|
className="form-check-label"
|
||||||
htmlFor="create-site-enable-nsfw"
|
htmlFor="create-site-enable-nsfw"
|
||||||
>
|
>
|
||||||
{i18n.t("enable_nsfw")}
|
{i18n.t("enable_nsfw")}
|
||||||
|
@ -291,11 +301,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="create-site-open-registration"
|
id="create-site-open-registration"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(this.state.siteForm.open_registration)}
|
checked={toUndefined(this.state.siteForm.open_registration)}
|
||||||
|
@ -305,7 +315,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
class="form-check-label"
|
className="form-check-label"
|
||||||
htmlFor="create-site-open-registration"
|
htmlFor="create-site-open-registration"
|
||||||
>
|
>
|
||||||
{i18n.t("open_registration")}
|
{i18n.t("open_registration")}
|
||||||
|
@ -313,11 +323,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="create-site-community-creation-admin-only"
|
id="create-site-community-creation-admin-only"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={toUndefined(
|
||||||
|
@ -329,7 +339,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
class="form-check-label"
|
className="form-check-label"
|
||||||
htmlFor="create-site-community-creation-admin-only"
|
htmlFor="create-site-community-creation-admin-only"
|
||||||
>
|
>
|
||||||
{i18n.t("community_creation_admin_only")}
|
{i18n.t("community_creation_admin_only")}
|
||||||
|
@ -337,11 +347,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="create-site-require-email-verification"
|
id="create-site-require-email-verification"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={toUndefined(
|
||||||
|
@ -353,7 +363,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
class="form-check-label"
|
className="form-check-label"
|
||||||
htmlFor="create-site-require-email-verification"
|
htmlFor="create-site-require-email-verification"
|
||||||
>
|
>
|
||||||
{i18n.t("require_email_verification")}
|
{i18n.t("require_email_verification")}
|
||||||
|
@ -361,18 +371,18 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="create-site-require-application"
|
id="create-site-require-application"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(this.state.siteForm.require_application)}
|
checked={toUndefined(this.state.siteForm.require_application)}
|
||||||
onChange={linkEvent(this, this.handleSiteRequireApplication)}
|
onChange={linkEvent(this, this.handleSiteRequireApplication)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
class="form-check-label"
|
className="form-check-label"
|
||||||
htmlFor="create-site-require-application"
|
htmlFor="create-site-require-application"
|
||||||
>
|
>
|
||||||
{i18n.t("require_registration_application")}
|
{i18n.t("require_registration_application")}
|
||||||
|
@ -380,10 +390,34 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
|
<div className="form-check">
|
||||||
|
<input
|
||||||
|
className="form-check-input"
|
||||||
|
id="create-site-application-email-admins"
|
||||||
|
type="checkbox"
|
||||||
|
checked={toUndefined(
|
||||||
|
this.state.siteForm.application_email_admins
|
||||||
|
)}
|
||||||
|
onChange={linkEvent(
|
||||||
|
this,
|
||||||
|
this.handleSiteApplicationEmailAdmins
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
className="form-check-label"
|
||||||
|
htmlFor="create-site-email-admins"
|
||||||
|
>
|
||||||
|
{i18n.t("application_email_admins")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-group row">
|
||||||
|
<div className="col-12">
|
||||||
<label
|
<label
|
||||||
class="form-check-label mr-2"
|
className="form-check-label mr-2"
|
||||||
htmlFor="create-site-default-theme"
|
htmlFor="create-site-default-theme"
|
||||||
>
|
>
|
||||||
{i18n.t("theme")}
|
{i18n.t("theme")}
|
||||||
|
@ -392,19 +426,21 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
id="create-site-default-theme"
|
id="create-site-default-theme"
|
||||||
value={toUndefined(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"
|
className="custom-select w-auto"
|
||||||
>
|
>
|
||||||
<option value="browser">{i18n.t("browser_default")}</option>
|
<option value="browser">{i18n.t("browser_default")}</option>
|
||||||
{this.state.themeList.unwrapOr([]).map(theme => (
|
{this.state.themeList.unwrapOr([]).map(theme => (
|
||||||
<option value={theme}>{theme}</option>
|
<option key={theme} value={theme}>
|
||||||
|
{theme}
|
||||||
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{this.props.showLocal && (
|
{this.props.showLocal && (
|
||||||
<form className="form-group row">
|
<form className="form-group row">
|
||||||
<label class="col-sm-3">{i18n.t("listing_type")}</label>
|
<label className="col-sm-3">{i18n.t("listing_type")}</label>
|
||||||
<div class="col-sm-9">
|
<div className="col-sm-9">
|
||||||
<ListingTypeSelect
|
<ListingTypeSelect
|
||||||
type_={
|
type_={
|
||||||
ListingType[
|
ListingType[
|
||||||
|
@ -420,18 +456,18 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="create-site-private-instance"
|
id="create-site-private-instance"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
value={toUndefined(this.state.siteForm.default_theme)}
|
checked={toUndefined(this.state.siteForm.private_instance)}
|
||||||
onChange={linkEvent(this, this.handleSitePrivateInstance)}
|
onChange={linkEvent(this, this.handleSitePrivateInstance)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
class="form-check-label"
|
className="form-check-label"
|
||||||
htmlFor="create-site-private-instance"
|
htmlFor="create-site-private-instance"
|
||||||
>
|
>
|
||||||
{i18n.t("private_instance")}
|
{i18n.t("private_instance")}
|
||||||
|
@ -439,11 +475,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="create-site-hide-modlog-mod-names"
|
id="create-site-hide-modlog-mod-names"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={toUndefined(
|
||||||
|
@ -452,7 +488,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
onChange={linkEvent(this, this.handleSiteHideModlogModNames)}
|
onChange={linkEvent(this, this.handleSiteHideModlogModNames)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
class="form-check-label"
|
className="form-check-label"
|
||||||
htmlFor="create-site-hide-modlog-mod-names"
|
htmlFor="create-site-hide-modlog-mod-names"
|
||||||
>
|
>
|
||||||
{i18n.t("hide_modlog_mod_names")}
|
{i18n.t("hide_modlog_mod_names")}
|
||||||
|
@ -460,11 +496,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-secondary mr-2"
|
className="btn btn-secondary mr-2"
|
||||||
disabled={this.state.loading}
|
disabled={this.state.loading}
|
||||||
>
|
>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
|
@ -478,7 +514,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
{this.props.site.isSome() && (
|
{this.props.site.isSome() && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
onClick={linkEvent(this, this.handleCancel)}
|
onClick={linkEvent(this, this.handleCancel)}
|
||||||
>
|
>
|
||||||
{i18n.t("cancel")}
|
{i18n.t("cancel")}
|
||||||
|
@ -493,8 +529,8 @@ 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.setState({ loading: true });
|
||||||
i.state.siteForm.auth = auth().unwrap();
|
i.setState(s => ((s.siteForm.auth = auth().unwrap()), s));
|
||||||
|
|
||||||
if (i.props.site.isSome()) {
|
if (i.props.site.isSome()) {
|
||||||
WebSocketService.Instance.send(wsClient.editSite(i.state.siteForm));
|
WebSocketService.Instance.send(wsClient.editSite(i.state.siteForm));
|
||||||
|
@ -517,6 +553,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
private_instance: sForm.private_instance,
|
private_instance: sForm.private_instance,
|
||||||
default_theme: sForm.default_theme,
|
default_theme: sForm.default_theme,
|
||||||
default_post_listing_type: sForm.default_post_listing_type,
|
default_post_listing_type: sForm.default_post_listing_type,
|
||||||
|
application_email_admins: sForm.application_email_admins,
|
||||||
auth: auth().unwrap(),
|
auth: auth().unwrap(),
|
||||||
hide_modlog_mod_names: sForm.hide_modlog_mod_names,
|
hide_modlog_mod_names: sForm.hide_modlog_mod_names,
|
||||||
});
|
});
|
||||||
|
@ -531,18 +568,15 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteSidebarChange(val: string) {
|
handleSiteSidebarChange(val: string) {
|
||||||
this.state.siteForm.sidebar = Some(val);
|
this.setState(s => ((s.siteForm.sidebar = Some(val)), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteLegalInfoChange(val: string) {
|
handleSiteLegalInfoChange(val: string) {
|
||||||
this.state.siteForm.legal_information = Some(val);
|
this.setState(s => ((s.siteForm.legal_information = Some(val)), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteApplicationQuestionChange(val: string) {
|
handleSiteApplicationQuestionChange(val: string) {
|
||||||
this.state.siteForm.application_question = Some(val);
|
this.setState(s => ((s.siteForm.application_question = Some(val)), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteDescChange(i: SiteForm, event: any) {
|
handleSiteDescChange(i: SiteForm, event: any) {
|
||||||
|
@ -580,6 +614,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSiteApplicationEmailAdmins(i: SiteForm, event: any) {
|
||||||
|
i.state.siteForm.application_email_admins = Some(event.target.checked);
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
|
|
||||||
handleSitePrivateInstance(i: SiteForm, event: any) {
|
handleSitePrivateInstance(i: SiteForm, event: any) {
|
||||||
i.state.siteForm.private_instance = Some(event.target.checked);
|
i.state.siteForm.private_instance = Some(event.target.checked);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
@ -600,29 +639,29 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleIconUpload(url: string) {
|
handleIconUpload(url: string) {
|
||||||
this.state.siteForm.icon = Some(url);
|
this.setState(s => ((s.siteForm.icon = Some(url)), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleIconRemove() {
|
handleIconRemove() {
|
||||||
this.state.siteForm.icon = Some("");
|
this.setState(s => ((s.siteForm.icon = Some("")), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerUpload(url: string) {
|
handleBannerUpload(url: string) {
|
||||||
this.state.siteForm.banner = Some(url);
|
this.setState(s => ((s.siteForm.banner = Some(url)), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerRemove() {
|
handleBannerRemove() {
|
||||||
this.state.siteForm.banner = Some("");
|
this.setState(s => ((s.siteForm.banner = Some("")), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDefaultPostListingTypeChange(val: ListingType) {
|
handleDefaultPostListingTypeChange(val: ListingType) {
|
||||||
this.state.siteForm.default_post_listing_type = Some(
|
this.setState(
|
||||||
ListingType[ListingType[val]]
|
s => (
|
||||||
|
(s.siteForm.default_post_listing_type = Some(
|
||||||
|
ListingType[ListingType[val]]
|
||||||
|
)),
|
||||||
|
s
|
||||||
|
)
|
||||||
);
|
);
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,11 +38,11 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
render() {
|
render() {
|
||||||
let site = this.props.site;
|
let site = this.props.site;
|
||||||
return (
|
return (
|
||||||
<div class="card border-secondary mb-3">
|
<div className="card border-secondary mb-3">
|
||||||
<div class="card-body">
|
<div className="card-body">
|
||||||
{!this.state.showEdit ? (
|
{!this.state.showEdit ? (
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-2">
|
<div className="mb-2">
|
||||||
{this.siteName()}
|
{this.siteName()}
|
||||||
{this.props.admins.isSome() && this.adminButtons()}
|
{this.props.admins.isSome() && this.adminButtons()}
|
||||||
</div>
|
</div>
|
||||||
|
@ -69,10 +69,10 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
siteName() {
|
siteName() {
|
||||||
let site = this.props.site;
|
let site = this.props.site;
|
||||||
return (
|
return (
|
||||||
<h5 class="mb-0 d-inline">
|
<h5 className="mb-0 d-inline">
|
||||||
{site.name}
|
{site.name}
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm text-muted"
|
className="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")}
|
||||||
|
@ -113,11 +113,11 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
|
|
||||||
adminButtons() {
|
adminButtons() {
|
||||||
return (
|
return (
|
||||||
amAdmin(this.props.admins) && (
|
amAdmin() && (
|
||||||
<ul class="list-inline mb-1 text-muted font-weight-bold">
|
<ul className="list-inline mb-1 text-muted font-weight-bold">
|
||||||
<li className="list-inline-item-action">
|
<li className="list-inline-item-action">
|
||||||
<button
|
<button
|
||||||
class="btn btn-link d-inline-block text-muted"
|
className="btn btn-link d-inline-block text-muted"
|
||||||
onClick={linkEvent(this, this.handleEditClick)}
|
onClick={linkEvent(this, this.handleEditClick)}
|
||||||
aria-label={i18n.t("edit")}
|
aria-label={i18n.t("edit")}
|
||||||
data-tippy-content={i18n.t("edit")}
|
data-tippy-content={i18n.t("edit")}
|
||||||
|
@ -138,10 +138,10 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
|
|
||||||
admins(admins: PersonViewSafe[]) {
|
admins(admins: PersonViewSafe[]) {
|
||||||
return (
|
return (
|
||||||
<ul class="mt-1 list-inline small mb-0">
|
<ul className="mt-1 list-inline small mb-0">
|
||||||
<li class="list-inline-item">{i18n.t("admins")}:</li>
|
<li className="list-inline-item">{i18n.t("admins")}:</li>
|
||||||
{admins.map(av => (
|
{admins.map(av => (
|
||||||
<li class="list-inline-item">
|
<li key={av.person.id} className="list-inline-item">
|
||||||
<PersonListing person={av.person} />
|
<PersonListing person={av.person} />
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
@ -153,7 +153,7 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
let counts = siteAggregates;
|
let counts = siteAggregates;
|
||||||
let online = this.props.online.unwrapOr(1);
|
let online = this.props.online.unwrapOr(1);
|
||||||
return (
|
return (
|
||||||
<ul class="my-2 list-inline">
|
<ul className="my-2 list-inline">
|
||||||
<li className="list-inline-item badge badge-secondary">
|
<li className="list-inline-item badge badge-secondary">
|
||||||
{i18n.t("number_online", {
|
{i18n.t("number_online", {
|
||||||
count: online,
|
count: online,
|
||||||
|
@ -246,22 +246,18 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCollapseSidebar(i: SiteSidebar) {
|
handleCollapseSidebar(i: SiteSidebar) {
|
||||||
i.state.collapsed = !i.state.collapsed;
|
i.setState({ collapsed: !i.state.collapsed });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditClick(i: SiteSidebar) {
|
handleEditClick(i: SiteSidebar) {
|
||||||
i.state.showEdit = true;
|
i.setState({ showEdit: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditSite() {
|
handleEditSite() {
|
||||||
this.state.showEdit = false;
|
this.setState({ showEdit: false });
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditCancel() {
|
handleEditCancel() {
|
||||||
this.state.showEdit = false;
|
this.setState({ showEdit: false });
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ import {
|
||||||
amAdmin,
|
amAdmin,
|
||||||
amMod,
|
amMod,
|
||||||
auth,
|
auth,
|
||||||
choicesModLogConfig,
|
choicesConfig,
|
||||||
debounce,
|
debounce,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
fetchUsers,
|
fetchUsers,
|
||||||
|
@ -114,31 +114,41 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
filter_user: None,
|
filter_user: None,
|
||||||
filter_mod: None,
|
filter_mod: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
this.handlePageChange = this.handlePageChange.bind(this);
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
|
|
||||||
this.state.communityId = this.props.match.params.community_id
|
|
||||||
? Some(Number(this.props.match.params.community_id))
|
|
||||||
: None;
|
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
communityId: this.props.match.params.community_id
|
||||||
|
? Some(Number(this.props.match.params.community_id))
|
||||||
|
: None,
|
||||||
|
};
|
||||||
|
|
||||||
// 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.res = Some(this.isoData.routeData[0] as GetModlogResponse);
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
res: Some(this.isoData.routeData[0] as GetModlogResponse),
|
||||||
|
};
|
||||||
|
|
||||||
if (this.isoData.routeData[1]) {
|
if (this.isoData.routeData[1]) {
|
||||||
// Getting the moderators
|
// Getting the moderators
|
||||||
let communityRes = Some(
|
let communityRes = Some(
|
||||||
this.isoData.routeData[1] as GetCommunityResponse
|
this.isoData.routeData[1] as GetCommunityResponse
|
||||||
);
|
);
|
||||||
this.state.communityMods = communityRes.map(c => c.moderators);
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
communityMods: communityRes.map(c => c.moderators),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state.loading = false;
|
this.state = { ...this.state, loading: false };
|
||||||
} else {
|
} else {
|
||||||
this.refetch();
|
this.refetch();
|
||||||
}
|
}
|
||||||
|
@ -300,219 +310,283 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
switch (i.type_) {
|
switch (i.type_) {
|
||||||
case ModlogActionType.ModRemovePost: {
|
case ModlogActionType.ModRemovePost: {
|
||||||
let mrpv = i.view as ModRemovePostView;
|
let mrpv = i.view as ModRemovePostView;
|
||||||
return [
|
return (
|
||||||
mrpv.mod_remove_post.removed.unwrapOr(false)
|
<>
|
||||||
? "Removed "
|
<span>
|
||||||
: "Restored ",
|
{mrpv.mod_remove_post.removed.unwrapOr(false)
|
||||||
<span>
|
? "Removed "
|
||||||
Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
|
: "Restored "}
|
||||||
</span>,
|
</span>
|
||||||
mrpv.mod_remove_post.reason.match({
|
<span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
|
||||||
none: <></>,
|
</span>
|
||||||
}),
|
<span>
|
||||||
];
|
{mrpv.mod_remove_post.reason.match({
|
||||||
|
some: reason => <div>reason: {reason}</div>,
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.ModLockPost: {
|
case ModlogActionType.ModLockPost: {
|
||||||
let mlpv = i.view as ModLockPostView;
|
let mlpv = i.view as ModLockPostView;
|
||||||
return [
|
return (
|
||||||
mlpv.mod_lock_post.locked.unwrapOr(false) ? "Locked " : "Unlocked ",
|
<>
|
||||||
<span>
|
<span>
|
||||||
Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
|
{mlpv.mod_lock_post.locked.unwrapOr(false)
|
||||||
</span>,
|
? "Locked "
|
||||||
];
|
: "Unlocked "}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.ModStickyPost: {
|
case ModlogActionType.ModStickyPost: {
|
||||||
let mspv = i.view as ModStickyPostView;
|
let mspv = i.view as ModStickyPostView;
|
||||||
return [
|
return (
|
||||||
mspv.mod_sticky_post.stickied.unwrapOr(false)
|
<>
|
||||||
? "Stickied "
|
<span>
|
||||||
: "Unstickied ",
|
{mspv.mod_sticky_post.stickied.unwrapOr(false)
|
||||||
<span>
|
? "Stickied "
|
||||||
Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
|
: "Unstickied "}
|
||||||
</span>,
|
</span>
|
||||||
];
|
<span>
|
||||||
|
Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link>
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.ModRemoveComment: {
|
case ModlogActionType.ModRemoveComment: {
|
||||||
let mrc = i.view as ModRemoveCommentView;
|
let mrc = i.view as ModRemoveCommentView;
|
||||||
return [
|
return (
|
||||||
mrc.mod_remove_comment.removed.unwrapOr(false)
|
<>
|
||||||
? "Removed "
|
<span>
|
||||||
: "Restored ",
|
{mrc.mod_remove_comment.removed.unwrapOr(false)
|
||||||
<span>
|
? "Removed "
|
||||||
Comment{" "}
|
: "Restored "}
|
||||||
<Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}>
|
</span>
|
||||||
{mrc.comment.content}
|
<span>
|
||||||
</Link>
|
Comment{" "}
|
||||||
</span>,
|
<Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}>
|
||||||
<span>
|
{mrc.comment.content}
|
||||||
{" "}
|
</Link>
|
||||||
by <PersonListing person={mrc.commenter} />
|
</span>
|
||||||
</span>,
|
<span>
|
||||||
mrc.mod_remove_comment.reason.match({
|
{" "}
|
||||||
some: reason => <div>reason: {reason}</div>,
|
by <PersonListing person={mrc.commenter} />
|
||||||
none: <></>,
|
</span>
|
||||||
}),
|
<span>
|
||||||
];
|
{mrc.mod_remove_comment.reason.match({
|
||||||
|
some: reason => <div>reason: {reason}</div>,
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.ModRemoveCommunity: {
|
case ModlogActionType.ModRemoveCommunity: {
|
||||||
let mrco = i.view as ModRemoveCommunityView;
|
let mrco = i.view as ModRemoveCommunityView;
|
||||||
return [
|
return (
|
||||||
mrco.mod_remove_community.removed.unwrapOr(false)
|
<>
|
||||||
? "Removed "
|
<span>
|
||||||
: "Restored ",
|
{mrco.mod_remove_community.removed.unwrapOr(false)
|
||||||
<span>
|
? "Removed "
|
||||||
Community <CommunityLink community={mrco.community} />
|
: "Restored "}
|
||||||
</span>,
|
</span>
|
||||||
mrco.mod_remove_community.reason.match({
|
<span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
Community <CommunityLink community={mrco.community} />
|
||||||
none: <></>,
|
</span>
|
||||||
}),
|
<span>
|
||||||
mrco.mod_remove_community.expires.match({
|
{mrco.mod_remove_community.reason.match({
|
||||||
some: expires => (
|
some: reason => <div>reason: {reason}</div>,
|
||||||
<div>expires: {moment.utc(expires).fromNow()}</div>
|
none: <></>,
|
||||||
),
|
})}
|
||||||
none: <></>,
|
</span>
|
||||||
}),
|
<span>
|
||||||
];
|
{mrco.mod_remove_community.expires.match({
|
||||||
|
some: expires => (
|
||||||
|
<div>expires: {moment.utc(expires).fromNow()}</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.ModBanFromCommunity: {
|
case ModlogActionType.ModBanFromCommunity: {
|
||||||
let mbfc = i.view as ModBanFromCommunityView;
|
let mbfc = i.view as ModBanFromCommunityView;
|
||||||
return [
|
return (
|
||||||
<span>
|
<>
|
||||||
{mbfc.mod_ban_from_community.banned.unwrapOr(false)
|
<span>
|
||||||
? "Banned "
|
{mbfc.mod_ban_from_community.banned.unwrapOr(false)
|
||||||
: "Unbanned "}{" "}
|
? "Banned "
|
||||||
</span>,
|
: "Unbanned "}{" "}
|
||||||
<span>
|
</span>
|
||||||
<PersonListing person={mbfc.banned_person} />
|
<span>
|
||||||
</span>,
|
<PersonListing person={mbfc.banned_person} />
|
||||||
<span> from the community </span>,
|
</span>
|
||||||
<span>
|
<span> from the community </span>
|
||||||
<CommunityLink community={mbfc.community} />
|
<span>
|
||||||
</span>,
|
<CommunityLink community={mbfc.community} />
|
||||||
mbfc.mod_ban_from_community.reason.match({
|
</span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
<span>
|
||||||
none: <></>,
|
{mbfc.mod_ban_from_community.reason.match({
|
||||||
}),
|
some: reason => <div>reason: {reason}</div>,
|
||||||
mbfc.mod_ban_from_community.expires.match({
|
none: <></>,
|
||||||
some: expires => (
|
})}
|
||||||
<div>expires: {moment.utc(expires).fromNow()}</div>
|
</span>
|
||||||
),
|
<span>
|
||||||
none: <></>,
|
{mbfc.mod_ban_from_community.expires.match({
|
||||||
}),
|
some: expires => (
|
||||||
];
|
<div>expires: {moment.utc(expires).fromNow()}</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.ModAddCommunity: {
|
case ModlogActionType.ModAddCommunity: {
|
||||||
let mac = i.view as ModAddCommunityView;
|
let mac = i.view as ModAddCommunityView;
|
||||||
return [
|
return (
|
||||||
<span>
|
<>
|
||||||
{mac.mod_add_community.removed.unwrapOr(false)
|
<span>
|
||||||
? "Removed "
|
{mac.mod_add_community.removed.unwrapOr(false)
|
||||||
: "Appointed "}{" "}
|
? "Removed "
|
||||||
</span>,
|
: "Appointed "}{" "}
|
||||||
<span>
|
</span>
|
||||||
<PersonListing person={mac.modded_person} />
|
<span>
|
||||||
</span>,
|
<PersonListing person={mac.modded_person} />
|
||||||
<span> as a mod to the community </span>,
|
</span>
|
||||||
<span>
|
<span> as a mod to the community </span>
|
||||||
<CommunityLink community={mac.community} />
|
<span>
|
||||||
</span>,
|
<CommunityLink community={mac.community} />
|
||||||
];
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.ModTransferCommunity: {
|
case ModlogActionType.ModTransferCommunity: {
|
||||||
let mtc = i.view as ModTransferCommunityView;
|
let mtc = i.view as ModTransferCommunityView;
|
||||||
return [
|
return (
|
||||||
<span>
|
<>
|
||||||
{mtc.mod_transfer_community.removed.unwrapOr(false)
|
<span>
|
||||||
? "Removed "
|
{mtc.mod_transfer_community.removed.unwrapOr(false)
|
||||||
: "Transferred "}{" "}
|
? "Removed "
|
||||||
</span>,
|
: "Transferred "}{" "}
|
||||||
<span>
|
</span>
|
||||||
<CommunityLink community={mtc.community} />
|
<span>
|
||||||
</span>,
|
<CommunityLink community={mtc.community} />
|
||||||
<span> to </span>,
|
</span>
|
||||||
<span>
|
<span> to </span>
|
||||||
<PersonListing person={mtc.modded_person} />
|
<span>
|
||||||
</span>,
|
<PersonListing person={mtc.modded_person} />
|
||||||
];
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.ModBan: {
|
case ModlogActionType.ModBan: {
|
||||||
let mb = i.view as ModBanView;
|
let mb = i.view as ModBanView;
|
||||||
return [
|
return (
|
||||||
<span>
|
<>
|
||||||
{mb.mod_ban.banned.unwrapOr(false) ? "Banned " : "Unbanned "}{" "}
|
<span>
|
||||||
</span>,
|
{mb.mod_ban.banned.unwrapOr(false) ? "Banned " : "Unbanned "}{" "}
|
||||||
<span>
|
</span>
|
||||||
<PersonListing person={mb.banned_person} />
|
<span>
|
||||||
</span>,
|
<PersonListing person={mb.banned_person} />
|
||||||
mb.mod_ban.reason.match({
|
</span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
<span>
|
||||||
none: <></>,
|
{mb.mod_ban.reason.match({
|
||||||
}),
|
some: reason => <div>reason: {reason}</div>,
|
||||||
mb.mod_ban.expires.match({
|
none: <></>,
|
||||||
some: expires => (
|
})}
|
||||||
<div>expires: {moment.utc(expires).fromNow()}</div>
|
</span>
|
||||||
),
|
<span>
|
||||||
none: <></>,
|
{mb.mod_ban.expires.match({
|
||||||
}),
|
some: expires => (
|
||||||
];
|
<div>expires: {moment.utc(expires).fromNow()}</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.ModAdd: {
|
case ModlogActionType.ModAdd: {
|
||||||
let ma = i.view as ModAddView;
|
let ma = i.view as ModAddView;
|
||||||
return [
|
return (
|
||||||
<span>
|
<>
|
||||||
{ma.mod_add.removed.unwrapOr(false) ? "Removed " : "Appointed "}{" "}
|
<span>
|
||||||
</span>,
|
{ma.mod_add.removed.unwrapOr(false) ? "Removed " : "Appointed "}{" "}
|
||||||
<span>
|
</span>
|
||||||
<PersonListing person={ma.modded_person} />
|
<span>
|
||||||
</span>,
|
<PersonListing person={ma.modded_person} />
|
||||||
<span> as an admin </span>,
|
</span>
|
||||||
];
|
<span> as an admin </span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.AdminPurgePerson: {
|
case ModlogActionType.AdminPurgePerson: {
|
||||||
let ap = i.view as AdminPurgePersonView;
|
let ap = i.view as AdminPurgePersonView;
|
||||||
return [
|
return (
|
||||||
<span>Purged a Person</span>,
|
<>
|
||||||
ap.admin_purge_person.reason.match({
|
<span>Purged a Person</span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
<span>
|
||||||
none: <></>,
|
{ap.admin_purge_person.reason.match({
|
||||||
}),
|
some: reason => <div>reason: {reason}</div>,
|
||||||
];
|
none: <></>,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.AdminPurgeCommunity: {
|
case ModlogActionType.AdminPurgeCommunity: {
|
||||||
let ap = i.view as AdminPurgeCommunityView;
|
let ap = i.view as AdminPurgeCommunityView;
|
||||||
return [
|
return (
|
||||||
<span>Purged a Community</span>,
|
<>
|
||||||
ap.admin_purge_community.reason.match({
|
<span>Purged a Community</span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
<span>
|
||||||
none: <></>,
|
{ap.admin_purge_community.reason.match({
|
||||||
}),
|
some: reason => <div>reason: {reason}</div>,
|
||||||
];
|
none: <></>,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.AdminPurgePost: {
|
case ModlogActionType.AdminPurgePost: {
|
||||||
let ap = i.view as AdminPurgePostView;
|
let ap = i.view as AdminPurgePostView;
|
||||||
return [
|
return (
|
||||||
<span>Purged a Post from from </span>,
|
<>
|
||||||
<CommunityLink community={ap.community} />,
|
<span>Purged a Post from from </span>
|
||||||
ap.admin_purge_post.reason.match({
|
<CommunityLink community={ap.community} />
|
||||||
some: reason => <div>reason: {reason}</div>,
|
<span>
|
||||||
none: <></>,
|
{ap.admin_purge_post.reason.match({
|
||||||
}),
|
some: reason => <div>reason: {reason}</div>,
|
||||||
];
|
none: <></>,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.AdminPurgeComment: {
|
case ModlogActionType.AdminPurgeComment: {
|
||||||
let ap = i.view as AdminPurgeCommentView;
|
let ap = i.view as AdminPurgeCommentView;
|
||||||
return [
|
return (
|
||||||
<span>
|
<>
|
||||||
Purged a Comment from{" "}
|
<span>
|
||||||
<Link to={`/post/${ap.post.id}`}>{ap.post.name}</Link>
|
Purged a Comment from{" "}
|
||||||
</span>,
|
<Link to={`/post/${ap.post.id}`}>{ap.post.name}</Link>
|
||||||
ap.admin_purge_comment.reason.match({
|
</span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
<span>
|
||||||
none: <></>,
|
{ap.admin_purge_comment.reason.match({
|
||||||
}),
|
some: reason => <div>reason: {reason}</div>,
|
||||||
];
|
none: <></>,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return <div />;
|
return <div />;
|
||||||
|
@ -525,7 +599,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
return (
|
return (
|
||||||
<tbody>
|
<tbody>
|
||||||
{combined.map(i => (
|
{combined.map(i => (
|
||||||
<tr>
|
<tr key={i.id}>
|
||||||
<td>
|
<td>
|
||||||
<MomentTime published={i.when_} updated={None} />
|
<MomentTime published={i.when_} updated={None} />
|
||||||
</td>
|
</td>
|
||||||
|
@ -544,10 +618,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get amAdminOrMod(): boolean {
|
get amAdminOrMod(): boolean {
|
||||||
return (
|
return amAdmin() || amMod(this.state.communityMods);
|
||||||
amAdmin(Some(this.state.siteRes.admins)) ||
|
|
||||||
amMod(this.state.communityMods)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
modOrAdminText(person: Option<PersonSafe>): string {
|
modOrAdminText(person: Option<PersonSafe>): string {
|
||||||
|
@ -593,67 +664,75 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
})}
|
})}
|
||||||
<span>{i18n.t("modlog")}</span>
|
<span>{i18n.t("modlog")}</span>
|
||||||
</h5>
|
</h5>
|
||||||
<form className="form-inline mr-2">
|
<div className="form-row">
|
||||||
<select
|
<div className="form-group col-sm-6">
|
||||||
value={this.state.filter_action}
|
<select
|
||||||
onChange={linkEvent(this, this.handleFilterActionChange)}
|
value={this.state.filter_action}
|
||||||
className="custom-select col-4 mb-2"
|
onChange={linkEvent(this, this.handleFilterActionChange)}
|
||||||
aria-label="action"
|
className="custom-select mb-2"
|
||||||
>
|
aria-label="action"
|
||||||
<option disabled aria-hidden="true">
|
>
|
||||||
{i18n.t("filter_by_action")}
|
<option disabled aria-hidden="true">
|
||||||
</option>
|
{i18n.t("filter_by_action")}
|
||||||
<option value={ModlogActionType.All}>{i18n.t("all")}</option>
|
</option>
|
||||||
<option value={ModlogActionType.ModRemovePost}>
|
<option value={ModlogActionType.All}>{i18n.t("all")}</option>
|
||||||
Removing Posts
|
<option value={ModlogActionType.ModRemovePost}>
|
||||||
</option>
|
Removing Posts
|
||||||
<option value={ModlogActionType.ModLockPost}>
|
</option>
|
||||||
Locking Posts
|
<option value={ModlogActionType.ModLockPost}>
|
||||||
</option>
|
Locking Posts
|
||||||
<option value={ModlogActionType.ModStickyPost}>
|
</option>
|
||||||
Stickying Posts
|
<option value={ModlogActionType.ModStickyPost}>
|
||||||
</option>
|
Stickying Posts
|
||||||
<option value={ModlogActionType.ModRemoveComment}>
|
</option>
|
||||||
Removing Comments
|
<option value={ModlogActionType.ModRemoveComment}>
|
||||||
</option>
|
Removing Comments
|
||||||
<option value={ModlogActionType.ModRemoveCommunity}>
|
</option>
|
||||||
Removing Communities
|
<option value={ModlogActionType.ModRemoveCommunity}>
|
||||||
</option>
|
Removing Communities
|
||||||
<option value={ModlogActionType.ModBanFromCommunity}>
|
</option>
|
||||||
Banning From Communities
|
<option value={ModlogActionType.ModBanFromCommunity}>
|
||||||
</option>
|
Banning From Communities
|
||||||
<option value={ModlogActionType.ModAddCommunity}>
|
</option>
|
||||||
Adding Mod to Community
|
<option value={ModlogActionType.ModAddCommunity}>
|
||||||
</option>
|
Adding Mod to Community
|
||||||
<option value={ModlogActionType.ModTransferCommunity}>
|
</option>
|
||||||
Transfering Communities
|
<option value={ModlogActionType.ModTransferCommunity}>
|
||||||
</option>
|
Transfering Communities
|
||||||
<option value={ModlogActionType.ModAdd}>
|
</option>
|
||||||
Adding Mod to Site
|
<option value={ModlogActionType.ModAdd}>
|
||||||
</option>
|
Adding Mod to Site
|
||||||
<option value={ModlogActionType.ModBan}>
|
</option>
|
||||||
Banning From Site
|
<option value={ModlogActionType.ModBan}>
|
||||||
</option>
|
Banning From Site
|
||||||
</select>
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
{this.state.siteRes.site_view.match({
|
{this.state.siteRes.site_view.match({
|
||||||
some: site_view =>
|
some: site_view =>
|
||||||
!site_view.site.hide_modlog_mod_names.unwrapOr(false) && (
|
!site_view.site.hide_modlog_mod_names.unwrapOr(false) && (
|
||||||
<select
|
<div className="form-group col-sm-6">
|
||||||
id="filter-mod"
|
<select
|
||||||
value={toUndefined(this.state.filter_mod)}
|
id="filter-mod"
|
||||||
>
|
className="form-control"
|
||||||
<option>{i18n.t("filter_by_mod")}</option>
|
value={toUndefined(this.state.filter_mod)}
|
||||||
</select>
|
>
|
||||||
|
<option>{i18n.t("filter_by_mod")}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
),
|
),
|
||||||
none: <></>,
|
none: <></>,
|
||||||
})}
|
})}
|
||||||
<select
|
<div className="form-group col-sm-6">
|
||||||
id="filter-user"
|
<select
|
||||||
value={toUndefined(this.state.filter_user)}
|
id="filter-user"
|
||||||
>
|
className="form-control"
|
||||||
<option>{i18n.t("filter_by_user")}</option>
|
value={toUndefined(this.state.filter_user)}
|
||||||
</select>
|
>
|
||||||
</form>
|
<option>{i18n.t("filter_by_user")}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="table-responsive">
|
<div className="table-responsive">
|
||||||
<table id="modlog_table" className="table table-sm table-hover">
|
<table id="modlog_table" className="table table-sm table-hover">
|
||||||
<thead className="pointer">
|
<thead className="pointer">
|
||||||
|
@ -715,12 +794,11 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
let selectId: any = document.getElementById("filter-user");
|
let selectId: any = document.getElementById("filter-user");
|
||||||
if (selectId) {
|
if (selectId) {
|
||||||
this.userChoices = new Choices(selectId, choicesModLogConfig);
|
this.userChoices = new Choices(selectId, choicesConfig);
|
||||||
this.userChoices.passedElement.element.addEventListener(
|
this.userChoices.passedElement.element.addEventListener(
|
||||||
"choice",
|
"choice",
|
||||||
(e: any) => {
|
(e: any) => {
|
||||||
this.state.filter_user = Some(Number(e.detail.choice.value));
|
this.setState({ filter_user: Some(Number(e.detail.choice.value)) });
|
||||||
this.setState(this.state);
|
|
||||||
this.refetch();
|
this.refetch();
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
|
@ -755,12 +833,11 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
let selectId: any = document.getElementById("filter-mod");
|
let selectId: any = document.getElementById("filter-mod");
|
||||||
if (selectId) {
|
if (selectId) {
|
||||||
this.modChoices = new Choices(selectId, choicesModLogConfig);
|
this.modChoices = new Choices(selectId, choicesConfig);
|
||||||
this.modChoices.passedElement.element.addEventListener(
|
this.modChoices.passedElement.element.addEventListener(
|
||||||
"choice",
|
"choice",
|
||||||
(e: any) => {
|
(e: any) => {
|
||||||
this.state.filter_mod = Some(Number(e.detail.choice.value));
|
this.setState({ filter_mod: Some(Number(e.detail.choice.value)) });
|
||||||
this.setState(this.state);
|
|
||||||
this.refetch();
|
this.refetch();
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
|
@ -829,14 +906,16 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.GetModlog) {
|
} else if (op == UserOperation.GetModlog) {
|
||||||
let data = wsJsonToRes<GetModlogResponse>(msg, GetModlogResponse);
|
let data = wsJsonToRes<GetModlogResponse>(msg, GetModlogResponse);
|
||||||
this.state.loading = false;
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.state.res = Some(data);
|
this.setState({ res: Some(data), loading: false });
|
||||||
this.setState(this.state);
|
this.setupUserFilter();
|
||||||
|
this.setupModFilter();
|
||||||
} else if (op == UserOperation.GetCommunity) {
|
} else if (op == UserOperation.GetCommunity) {
|
||||||
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
||||||
this.state.communityMods = Some(data.moderators);
|
this.setState({
|
||||||
this.state.communityName = Some(data.community_view.community.name);
|
communityMods: Some(data.moderators),
|
||||||
|
communityName: Some(data.community_view.community.name),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
PersonMentionResponse,
|
PersonMentionResponse,
|
||||||
PersonMentionView,
|
PersonMentionView,
|
||||||
PostReportResponse,
|
PostReportResponse,
|
||||||
|
PrivateMessageReportResponse,
|
||||||
PrivateMessageResponse,
|
PrivateMessageResponse,
|
||||||
PrivateMessagesResponse,
|
PrivateMessagesResponse,
|
||||||
PrivateMessageView,
|
PrivateMessageView,
|
||||||
|
@ -127,15 +128,19 @@ 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.state = {
|
||||||
(this.isoData.routeData[0] as GetRepliesResponse).replies || [];
|
...this.state,
|
||||||
this.state.mentions =
|
replies:
|
||||||
(this.isoData.routeData[1] as GetPersonMentionsResponse).mentions || [];
|
(this.isoData.routeData[0] as GetRepliesResponse).replies || [],
|
||||||
this.state.messages =
|
mentions:
|
||||||
(this.isoData.routeData[2] as PrivateMessagesResponse)
|
(this.isoData.routeData[1] as GetPersonMentionsResponse).mentions ||
|
||||||
.private_messages || [];
|
[],
|
||||||
this.state.combined = this.buildCombined();
|
messages:
|
||||||
this.state.loading = false;
|
(this.isoData.routeData[2] as PrivateMessagesResponse)
|
||||||
|
.private_messages || [],
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
|
this.state = { ...this.state, combined: this.buildCombined() };
|
||||||
} else {
|
} else {
|
||||||
this.refetch();
|
this.refetch();
|
||||||
}
|
}
|
||||||
|
@ -166,21 +171,21 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
.ok()
|
.ok()
|
||||||
.map(a => `/feeds/inbox/${a}.xml`);
|
.map(a => `/feeds/inbox/${a}.xml`);
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<h5>
|
<h5>
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
</h5>
|
</h5>
|
||||||
) : (
|
) : (
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
description={None}
|
||||||
image={None}
|
image={None}
|
||||||
/>
|
/>
|
||||||
<h5 class="mb-2">
|
<h5 className="mb-2">
|
||||||
{i18n.t("inbox")}
|
{i18n.t("inbox")}
|
||||||
{inboxRss.match({
|
{inboxRss.match({
|
||||||
some: rss => (
|
some: rss => (
|
||||||
|
@ -204,7 +209,7 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
0 &&
|
0 &&
|
||||||
this.state.unreadOrAll == UnreadOrAll.Unread && (
|
this.state.unreadOrAll == UnreadOrAll.Unread && (
|
||||||
<button
|
<button
|
||||||
class="btn btn-secondary mb-2"
|
className="btn btn-secondary mb-2"
|
||||||
onClick={linkEvent(this, this.markAllAsRead)}
|
onClick={linkEvent(this, this.markAllAsRead)}
|
||||||
>
|
>
|
||||||
{i18n.t("mark_all_as_read")}
|
{i18n.t("mark_all_as_read")}
|
||||||
|
@ -230,7 +235,7 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
|
|
||||||
unreadOrAllRadios() {
|
unreadOrAllRadios() {
|
||||||
return (
|
return (
|
||||||
<div class="btn-group btn-group-toggle flex-wrap mb-2">
|
<div className="btn-group btn-group-toggle flex-wrap mb-2">
|
||||||
<label
|
<label
|
||||||
className={`btn btn-outline-secondary pointer
|
className={`btn btn-outline-secondary pointer
|
||||||
${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
|
${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
|
||||||
|
@ -263,7 +268,7 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
|
|
||||||
messageTypeRadios() {
|
messageTypeRadios() {
|
||||||
return (
|
return (
|
||||||
<div class="btn-group btn-group-toggle flex-wrap mb-2">
|
<div className="btn-group btn-group-toggle flex-wrap mb-2">
|
||||||
<label
|
<label
|
||||||
className={`btn btn-outline-secondary pointer
|
className={`btn btn-outline-secondary pointer
|
||||||
${this.state.messageType == MessageType.All && "active"}
|
${this.state.messageType == MessageType.All && "active"}
|
||||||
|
@ -323,8 +328,8 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
selects() {
|
selects() {
|
||||||
return (
|
return (
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<span class="mr-3">{this.unreadOrAllRadios()}</span>
|
<span className="mr-3">{this.unreadOrAllRadios()}</span>
|
||||||
<span class="mr-3">{this.messageTypeRadios()}</span>
|
<span className="mr-3">{this.messageTypeRadios()}</span>
|
||||||
<CommentSortSelect
|
<CommentSortSelect
|
||||||
sort={this.state.sort}
|
sort={this.state.sort}
|
||||||
onChange={this.handleSortChange}
|
onChange={this.handleSortChange}
|
||||||
|
@ -394,6 +399,7 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case ReplyEnum.Mention:
|
case ReplyEnum.Mention:
|
||||||
|
@ -416,6 +422,7 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case ReplyEnum.Message:
|
case ReplyEnum.Message:
|
||||||
|
@ -448,6 +455,7 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -469,6 +477,7 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -494,16 +503,12 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnreadOrAllChange(i: Inbox, event: any) {
|
handleUnreadOrAllChange(i: Inbox, event: any) {
|
||||||
i.state.unreadOrAll = Number(event.target.value);
|
i.setState({ unreadOrAll: Number(event.target.value), page: 1 });
|
||||||
i.state.page = 1;
|
|
||||||
i.setState(i.state);
|
|
||||||
i.refetch();
|
i.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMessageTypeChange(i: Inbox, event: any) {
|
handleMessageTypeChange(i: Inbox, event: any) {
|
||||||
i.state.messageType = Number(event.target.value);
|
i.setState({ messageType: Number(event.target.value), page: 1 });
|
||||||
i.state.page = 1;
|
|
||||||
i.setState(i.state);
|
|
||||||
i.refetch();
|
i.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -580,9 +585,7 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSortChange(val: CommentSortType) {
|
handleSortChange(val: CommentSortType) {
|
||||||
this.state.sort = val;
|
this.setState({ sort: val, page: 1 });
|
||||||
this.state.page = 1;
|
|
||||||
this.setState(this.state);
|
|
||||||
this.refetch();
|
this.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -592,10 +595,8 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
auth: auth().unwrap(),
|
auth: auth().unwrap(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
i.state.replies = [];
|
i.setState({ replies: [], mentions: [], messages: [] });
|
||||||
i.state.mentions = [];
|
i.setState({ combined: i.buildCombined() });
|
||||||
i.state.messages = [];
|
|
||||||
i.state.combined = i.buildCombined();
|
|
||||||
UserService.Instance.unreadInboxCountSub.next(0);
|
UserService.Instance.unreadInboxCountSub.next(0);
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
@ -620,31 +621,27 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
this.refetch();
|
this.refetch();
|
||||||
} else if (op == UserOperation.GetReplies) {
|
} else if (op == UserOperation.GetReplies) {
|
||||||
let data = wsJsonToRes<GetRepliesResponse>(msg, GetRepliesResponse);
|
let data = wsJsonToRes<GetRepliesResponse>(msg, GetRepliesResponse);
|
||||||
this.state.replies = data.replies;
|
this.setState({ replies: data.replies });
|
||||||
this.state.combined = this.buildCombined();
|
this.setState({ combined: this.buildCombined(), loading: false });
|
||||||
this.state.loading = false;
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.setState(this.state);
|
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.GetPersonMentions) {
|
} else if (op == UserOperation.GetPersonMentions) {
|
||||||
let data = wsJsonToRes<GetPersonMentionsResponse>(
|
let data = wsJsonToRes<GetPersonMentionsResponse>(
|
||||||
msg,
|
msg,
|
||||||
GetPersonMentionsResponse
|
GetPersonMentionsResponse
|
||||||
);
|
);
|
||||||
this.state.mentions = data.mentions;
|
this.setState({ mentions: data.mentions });
|
||||||
this.state.combined = this.buildCombined();
|
this.setState({ combined: this.buildCombined() });
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.setState(this.state);
|
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.GetPrivateMessages) {
|
} else if (op == UserOperation.GetPrivateMessages) {
|
||||||
let data = wsJsonToRes<PrivateMessagesResponse>(
|
let data = wsJsonToRes<PrivateMessagesResponse>(
|
||||||
msg,
|
msg,
|
||||||
PrivateMessagesResponse
|
PrivateMessagesResponse
|
||||||
);
|
);
|
||||||
this.state.messages = data.private_messages;
|
this.setState({ messages: data.private_messages });
|
||||||
this.state.combined = this.buildCombined();
|
this.setState({ combined: this.buildCombined() });
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.setState(this.state);
|
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.EditPrivateMessage) {
|
} else if (op == UserOperation.EditPrivateMessage) {
|
||||||
let data = wsJsonToRes<PrivateMessageResponse>(
|
let data = wsJsonToRes<PrivateMessageResponse>(
|
||||||
|
@ -696,7 +693,9 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
let combinedView = this.state.combined.find(
|
let combinedView = this.state.combined.find(
|
||||||
i => i.id == data.private_message_view.private_message.id
|
i =>
|
||||||
|
i.id == data.private_message_view.private_message.id &&
|
||||||
|
i.type_ == ReplyEnum.Message
|
||||||
).view as PrivateMessageView;
|
).view as PrivateMessageView;
|
||||||
found.private_message.updated = combinedView.private_message.updated =
|
found.private_message.updated = combinedView.private_message.updated =
|
||||||
data.private_message_view.private_message.updated;
|
data.private_message_view.private_message.updated;
|
||||||
|
@ -706,14 +705,18 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
this.state.unreadOrAll == UnreadOrAll.Unread &&
|
this.state.unreadOrAll == UnreadOrAll.Unread &&
|
||||||
data.private_message_view.private_message.read
|
data.private_message_view.private_message.read
|
||||||
) {
|
) {
|
||||||
this.state.messages = this.state.messages.filter(
|
this.setState({
|
||||||
r =>
|
messages: this.state.messages.filter(
|
||||||
r.private_message.id !==
|
r =>
|
||||||
data.private_message_view.private_message.id
|
r.private_message.id !==
|
||||||
);
|
data.private_message_view.private_message.id
|
||||||
this.state.combined = this.state.combined.filter(
|
),
|
||||||
r => r.id !== data.private_message_view.private_message.id
|
});
|
||||||
);
|
this.setState({
|
||||||
|
combined: this.state.combined.filter(
|
||||||
|
r => r.id !== data.private_message_view.private_message.id
|
||||||
|
),
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
found.private_message.read = combinedView.private_message.read =
|
found.private_message.read = combinedView.private_message.read =
|
||||||
data.private_message_view.private_message.read;
|
data.private_message_view.private_message.read;
|
||||||
|
@ -733,7 +736,6 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.MarkCommentReplyAsRead) {
|
} else if (op == UserOperation.MarkCommentReplyAsRead) {
|
||||||
let data = wsJsonToRes<CommentReplyResponse>(msg, CommentReplyResponse);
|
let data = wsJsonToRes<CommentReplyResponse>(msg, CommentReplyResponse);
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
let found = this.state.replies.find(
|
let found = this.state.replies.find(
|
||||||
c => c.comment_reply.id == data.comment_reply_view.comment_reply.id
|
c => c.comment_reply.id == data.comment_reply_view.comment_reply.id
|
||||||
|
@ -741,7 +743,9 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
let combinedView = this.state.combined.find(
|
let combinedView = this.state.combined.find(
|
||||||
i => i.id == data.comment_reply_view.comment_reply.id
|
i =>
|
||||||
|
i.id == data.comment_reply_view.comment_reply.id &&
|
||||||
|
i.type_ == ReplyEnum.Reply
|
||||||
).view as CommentReplyView;
|
).view as CommentReplyView;
|
||||||
found.comment.content = combinedView.comment.content =
|
found.comment.content = combinedView.comment.content =
|
||||||
data.comment_reply_view.comment.content;
|
data.comment_reply_view.comment.content;
|
||||||
|
@ -763,12 +767,17 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
this.state.unreadOrAll == UnreadOrAll.Unread &&
|
this.state.unreadOrAll == UnreadOrAll.Unread &&
|
||||||
data.comment_reply_view.comment_reply.read
|
data.comment_reply_view.comment_reply.read
|
||||||
) {
|
) {
|
||||||
this.state.replies = this.state.replies.filter(
|
this.setState({
|
||||||
r => r.comment_reply.id !== data.comment_reply_view.comment_reply.id
|
replies: this.state.replies.filter(
|
||||||
);
|
r =>
|
||||||
this.state.combined = this.state.combined.filter(
|
r.comment_reply.id !== data.comment_reply_view.comment_reply.id
|
||||||
r => r.id !== data.comment_reply_view.comment_reply.id
|
),
|
||||||
);
|
});
|
||||||
|
this.setState({
|
||||||
|
combined: this.state.combined.filter(
|
||||||
|
r => r.id !== data.comment_reply_view.comment_reply.id
|
||||||
|
),
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
found.comment_reply.read = combinedView.comment_reply.read =
|
found.comment_reply.read = combinedView.comment_reply.read =
|
||||||
data.comment_reply_view.comment_reply.read;
|
data.comment_reply_view.comment_reply.read;
|
||||||
|
@ -786,7 +795,9 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
let combinedView = this.state.combined.find(
|
let combinedView = this.state.combined.find(
|
||||||
i => i.id == data.person_mention_view.person_mention.id
|
i =>
|
||||||
|
i.id == data.person_mention_view.person_mention.id &&
|
||||||
|
i.type_ == ReplyEnum.Mention
|
||||||
).view as PersonMentionView;
|
).view as PersonMentionView;
|
||||||
found.comment.content = combinedView.comment.content =
|
found.comment.content = combinedView.comment.content =
|
||||||
data.person_mention_view.comment.content;
|
data.person_mention_view.comment.content;
|
||||||
|
@ -808,13 +819,18 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
this.state.unreadOrAll == UnreadOrAll.Unread &&
|
this.state.unreadOrAll == UnreadOrAll.Unread &&
|
||||||
data.person_mention_view.person_mention.read
|
data.person_mention_view.person_mention.read
|
||||||
) {
|
) {
|
||||||
this.state.mentions = this.state.mentions.filter(
|
this.setState({
|
||||||
r =>
|
mentions: this.state.mentions.filter(
|
||||||
r.person_mention.id !== data.person_mention_view.person_mention.id
|
r =>
|
||||||
);
|
r.person_mention.id !==
|
||||||
this.state.combined = this.state.combined.filter(
|
data.person_mention_view.person_mention.id
|
||||||
r => r.id !== data.person_mention_view.person_mention.id
|
),
|
||||||
);
|
});
|
||||||
|
this.setState({
|
||||||
|
combined: this.state.combined.filter(
|
||||||
|
r => r.id !== data.person_mention_view.person_mention.id
|
||||||
|
),
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// TODO test to make sure these mentions are getting marked as read
|
// TODO test to make sure these mentions are getting marked as read
|
||||||
found.person_mention.read = combinedView.person_mention.read =
|
found.person_mention.read = combinedView.person_mention.read =
|
||||||
|
@ -865,6 +881,14 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
toast(i18n.t("report_created"));
|
||||||
}
|
}
|
||||||
|
} else if (op == UserOperation.CreatePrivateMessageReport) {
|
||||||
|
let data = wsJsonToRes<PrivateMessageReportResponse>(
|
||||||
|
msg,
|
||||||
|
PrivateMessageReportResponse
|
||||||
|
);
|
||||||
|
if (data) {
|
||||||
|
toast(i18n.t("report_created"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,15 +66,15 @@ export class PasswordChange extends Component<any, State> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="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}
|
description={None}
|
||||||
image={None}
|
image={None}
|
||||||
/>
|
/>
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
<div className="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||||
<h5>{i18n.t("password_change")}</h5>
|
<h5>{i18n.t("password_change")}</h5>
|
||||||
{this.passwordChangeForm()}
|
{this.passwordChangeForm()}
|
||||||
</div>
|
</div>
|
||||||
|
@ -86,41 +86,41 @@ export class PasswordChange extends Component<any, State> {
|
||||||
passwordChangeForm() {
|
passwordChangeForm() {
|
||||||
return (
|
return (
|
||||||
<form onSubmit={linkEvent(this, this.handlePasswordChangeSubmit)}>
|
<form onSubmit={linkEvent(this, this.handlePasswordChangeSubmit)}>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="new-password">
|
<label className="col-sm-2 col-form-label" htmlFor="new-password">
|
||||||
{i18n.t("new_password")}
|
{i18n.t("new_password")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<input
|
<input
|
||||||
id="new-password"
|
id="new-password"
|
||||||
type="password"
|
type="password"
|
||||||
value={this.state.passwordChangeForm.password}
|
value={this.state.passwordChangeForm.password}
|
||||||
onInput={linkEvent(this, this.handlePasswordChange)}
|
onInput={linkEvent(this, this.handlePasswordChange)}
|
||||||
class="form-control"
|
className="form-control"
|
||||||
required
|
required
|
||||||
maxLength={60}
|
maxLength={60}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="verify-password">
|
<label className="col-sm-2 col-form-label" htmlFor="verify-password">
|
||||||
{i18n.t("verify_password")}
|
{i18n.t("verify_password")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<input
|
<input
|
||||||
id="verify-password"
|
id="verify-password"
|
||||||
type="password"
|
type="password"
|
||||||
value={this.state.passwordChangeForm.password_verify}
|
value={this.state.passwordChangeForm.password_verify}
|
||||||
onInput={linkEvent(this, this.handleVerifyPasswordChange)}
|
onInput={linkEvent(this, this.handleVerifyPasswordChange)}
|
||||||
class="form-control"
|
className="form-control"
|
||||||
required
|
required
|
||||||
maxLength={60}
|
maxLength={60}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<button type="submit" class="btn btn-secondary">
|
<button type="submit" className="btn btn-secondary">
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : (
|
) : (
|
||||||
|
@ -145,8 +145,7 @@ export class PasswordChange extends Component<any, State> {
|
||||||
|
|
||||||
handlePasswordChangeSubmit(i: PasswordChange, event: any) {
|
handlePasswordChangeSubmit(i: PasswordChange, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.loading = true;
|
i.setState({ loading: true });
|
||||||
i.setState(i.state);
|
|
||||||
|
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.passwordChange(i.state.passwordChangeForm)
|
wsClient.passwordChange(i.state.passwordChangeForm)
|
||||||
|
@ -158,13 +157,11 @@ export class PasswordChange extends Component<any, State> {
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
this.state.loading = false;
|
this.setState({ loading: false });
|
||||||
this.setState(this.state);
|
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.PasswordChange) {
|
} else if (op == UserOperation.PasswordChange) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
||||||
this.state = this.emptyState;
|
this.setState(this.emptyState);
|
||||||
this.setState(this.state);
|
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
this.props.history.push("/");
|
this.props.history.push("/");
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Component } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommentView,
|
CommentView,
|
||||||
GetPersonDetailsResponse,
|
GetPersonDetailsResponse,
|
||||||
|
Language,
|
||||||
PersonViewSafe,
|
PersonViewSafe,
|
||||||
PostView,
|
PostView,
|
||||||
SortType,
|
SortType,
|
||||||
|
@ -16,6 +17,7 @@ import { PostListing } from "../post/post-listing";
|
||||||
interface PersonDetailsProps {
|
interface PersonDetailsProps {
|
||||||
personRes: GetPersonDetailsResponse;
|
personRes: GetPersonDetailsResponse;
|
||||||
admins: PersonViewSafe[];
|
admins: PersonViewSafe[];
|
||||||
|
allLanguages: Language[];
|
||||||
page: number;
|
page: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
sort: SortType;
|
sort: SortType;
|
||||||
|
@ -99,6 +101,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -114,6 +117,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
showCommunity
|
showCommunity
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -150,7 +154,10 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{combined.map(i => [this.renderItemType(i), <hr class="my-3" />])}
|
{combined.map(i => [
|
||||||
|
this.renderItemType(i),
|
||||||
|
<hr key={i.type_} className="my-3" />,
|
||||||
|
])}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -168,6 +175,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -186,8 +194,9 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
moderators={None}
|
moderators={None}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
/>
|
/>
|
||||||
<hr class="my-3" />
|
<hr className="my-3" />
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -121,15 +121,14 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
|
|
||||||
// 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.personRes = Some(
|
this.state = {
|
||||||
this.isoData.routeData[0] as GetPersonDetailsResponse
|
...this.state,
|
||||||
);
|
personRes: Some(this.isoData.routeData[0] as GetPersonDetailsResponse),
|
||||||
this.state.loading = false;
|
loading: false,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this.fetchUserData();
|
this.fetchUserData();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setPersonBlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchUserData() {
|
fetchUserData() {
|
||||||
|
@ -162,11 +161,12 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
UserService.Instance.myUserInfo.match({
|
UserService.Instance.myUserInfo.match({
|
||||||
some: mui =>
|
some: mui =>
|
||||||
this.state.personRes.match({
|
this.state.personRes.match({
|
||||||
some: res => {
|
some: res =>
|
||||||
this.state.personBlocked = mui.person_blocks
|
this.setState({
|
||||||
.map(a => a.target.id)
|
personBlocked: mui.person_blocks
|
||||||
.includes(res.person_view.person.id);
|
.map(a => a.target.id)
|
||||||
},
|
.includes(res.person_view.person.id),
|
||||||
|
}),
|
||||||
none: void 0,
|
none: void 0,
|
||||||
}),
|
}),
|
||||||
none: void 0,
|
none: void 0,
|
||||||
|
@ -207,6 +207,7 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
this.setPersonBlock();
|
||||||
setupTippy();
|
setupTippy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +251,7 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<h5>
|
<h5>
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
|
@ -258,8 +259,8 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
) : (
|
) : (
|
||||||
this.state.personRes.match({
|
this.state.personRes.match({
|
||||||
some: res => (
|
some: res => (
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12 col-md-8">
|
<div className="col-12 col-md-8">
|
||||||
<>
|
<>
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
|
@ -281,11 +282,12 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
view={this.state.view}
|
view={this.state.view}
|
||||||
onPageChange={this.handlePageChange}
|
onPageChange={this.handlePageChange}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!this.state.loading && (
|
{!this.state.loading && (
|
||||||
<div class="col-12 col-md-4">
|
<div className="col-12 col-md-4">
|
||||||
{this.moderates()}
|
{this.moderates()}
|
||||||
{this.amCurrentUser && this.follows()}
|
{this.amCurrentUser && this.follows()}
|
||||||
</div>
|
</div>
|
||||||
|
@ -301,7 +303,7 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
|
|
||||||
viewRadios() {
|
viewRadios() {
|
||||||
return (
|
return (
|
||||||
<div class="btn-group btn-group-toggle flex-wrap mb-2">
|
<div className="btn-group btn-group-toggle flex-wrap mb-2">
|
||||||
<label
|
<label
|
||||||
className={`btn btn-outline-secondary pointer
|
className={`btn btn-outline-secondary pointer
|
||||||
${this.state.view == PersonDetailsView.Overview && "active"}
|
${this.state.view == PersonDetailsView.Overview && "active"}
|
||||||
|
@ -363,7 +365,7 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<span class="mr-3">{this.viewRadios()}</span>
|
<span className="mr-3">{this.viewRadios()}</span>
|
||||||
<SortSelect
|
<SortSelect
|
||||||
sort={this.state.sort}
|
sort={this.state.sort}
|
||||||
onChange={this.handleSortChange}
|
onChange={this.handleSortChange}
|
||||||
|
@ -406,14 +408,17 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
banner={pv.person.banner}
|
banner={pv.person.banner}
|
||||||
icon={pv.person.avatar}
|
icon={pv.person.avatar}
|
||||||
/>
|
/>
|
||||||
<div class="mb-3">
|
<div className="mb-3">
|
||||||
<div class="">
|
<div className="">
|
||||||
<div class="mb-0 d-flex flex-wrap">
|
<div className="mb-0 d-flex flex-wrap">
|
||||||
<div>
|
<div>
|
||||||
{pv.person.display_name && (
|
{pv.person.display_name.match({
|
||||||
<h5 class="mb-0">{pv.person.display_name}</h5>
|
some: displayName => (
|
||||||
)}
|
<h5 className="mb-0">{displayName}</h5>
|
||||||
<ul class="list-inline mb-2">
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
|
<ul className="list-inline mb-2">
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<PersonListing
|
<PersonListing
|
||||||
person={pv.person}
|
person={pv.person}
|
||||||
|
@ -531,7 +536,7 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
none: <></>,
|
none: <></>,
|
||||||
})}
|
})}
|
||||||
<div>
|
<div>
|
||||||
<ul class="list-inline mb-2">
|
<ul className="list-inline mb-2">
|
||||||
<li className="list-inline-item badge badge-light">
|
<li className="list-inline-item badge badge-light">
|
||||||
{i18n.t("number_of_posts", {
|
{i18n.t("number_of_posts", {
|
||||||
count: pv.counts.post_count,
|
count: pv.counts.post_count,
|
||||||
|
@ -546,7 +551,7 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-muted">
|
<div className="text-muted">
|
||||||
{i18n.t("joined")}{" "}
|
{i18n.t("joined")}{" "}
|
||||||
<MomentTime
|
<MomentTime
|
||||||
published={pv.person.published}
|
published={pv.person.published}
|
||||||
|
@ -581,33 +586,36 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
<>
|
<>
|
||||||
{this.state.showBanDialog && (
|
{this.state.showBanDialog && (
|
||||||
<form onSubmit={linkEvent(this, this.handleModBanSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleModBanSubmit)}>
|
||||||
<div class="form-group row col-12">
|
<div className="form-group row col-12">
|
||||||
<label class="col-form-label" htmlFor="profile-ban-reason">
|
<label
|
||||||
|
className="col-form-label"
|
||||||
|
htmlFor="profile-ban-reason"
|
||||||
|
>
|
||||||
{i18n.t("reason")}
|
{i18n.t("reason")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="profile-ban-reason"
|
id="profile-ban-reason"
|
||||||
class="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("reason")}
|
placeholder={i18n.t("reason")}
|
||||||
value={toUndefined(this.state.banReason)}
|
value={toUndefined(this.state.banReason)}
|
||||||
onInput={linkEvent(this, this.handleModBanReasonChange)}
|
onInput={linkEvent(this, this.handleModBanReasonChange)}
|
||||||
/>
|
/>
|
||||||
<label class="col-form-label" htmlFor={`mod-ban-expires`}>
|
<label className="col-form-label" htmlFor={`mod-ban-expires`}>
|
||||||
{i18n.t("expires")}
|
{i18n.t("expires")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
id={`mod-ban-expires`}
|
id={`mod-ban-expires`}
|
||||||
class="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("number_of_days")}
|
placeholder={i18n.t("number_of_days")}
|
||||||
value={toUndefined(this.state.banExpireDays)}
|
value={toUndefined(this.state.banExpireDays)}
|
||||||
onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
|
onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
|
||||||
/>
|
/>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="mod-ban-remove-data"
|
id="mod-ban-remove-data"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.removeData}
|
checked={this.state.removeData}
|
||||||
|
@ -617,7 +625,7 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
class="form-check-label"
|
className="form-check-label"
|
||||||
htmlFor="mod-ban-remove-data"
|
htmlFor="mod-ban-remove-data"
|
||||||
title={i18n.t("remove_content_more")}
|
title={i18n.t("remove_content_more")}
|
||||||
>
|
>
|
||||||
|
@ -631,10 +639,10 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
{/* <label class="col-form-label">Expires</label> */}
|
{/* <label class="col-form-label">Expires</label> */}
|
||||||
{/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
|
{/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
|
||||||
{/* </div> */}
|
{/* </div> */}
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<button
|
<button
|
||||||
type="cancel"
|
type="reset"
|
||||||
class="btn btn-secondary mr-2"
|
className="btn btn-secondary mr-2"
|
||||||
aria-label={i18n.t("cancel")}
|
aria-label={i18n.t("cancel")}
|
||||||
onClick={linkEvent(this, this.handleModBanSubmitCancel)}
|
onClick={linkEvent(this, this.handleModBanSubmitCancel)}
|
||||||
>
|
>
|
||||||
|
@ -642,7 +650,7 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
aria-label={i18n.t("ban")}
|
aria-label={i18n.t("ban")}
|
||||||
>
|
>
|
||||||
{i18n.t("ban")} {pv.person.name}
|
{i18n.t("ban")} {pv.person.name}
|
||||||
|
@ -656,25 +664,28 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO test this, make sure its good
|
|
||||||
moderates() {
|
moderates() {
|
||||||
return this.state.personRes
|
return this.state.personRes
|
||||||
.map(r => r.moderates)
|
.map(r => r.moderates)
|
||||||
.match({
|
.match({
|
||||||
some: moderates => {
|
some: moderates => {
|
||||||
if (moderates.length > 0) {
|
if (moderates.length > 0) {
|
||||||
<div class="card border-secondary mb-3">
|
return (
|
||||||
<div class="card-body">
|
<div className="card border-secondary mb-3">
|
||||||
<h5>{i18n.t("moderates")}</h5>
|
<div className="card-body">
|
||||||
<ul class="list-unstyled mb-0">
|
<h5>{i18n.t("moderates")}</h5>
|
||||||
{moderates.map(cmv => (
|
<ul className="list-unstyled mb-0">
|
||||||
<li>
|
{moderates.map(cmv => (
|
||||||
<CommunityLink community={cmv.community} />
|
<li key={cmv.community.id}>
|
||||||
</li>
|
<CommunityLink community={cmv.community} />
|
||||||
))}
|
</li>
|
||||||
</ul>
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
);
|
||||||
|
} else {
|
||||||
|
return <></>;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
none: void 0,
|
none: void 0,
|
||||||
|
@ -687,18 +698,22 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
.match({
|
.match({
|
||||||
some: follows => {
|
some: follows => {
|
||||||
if (follows.length > 0) {
|
if (follows.length > 0) {
|
||||||
<div class="card border-secondary mb-3">
|
return (
|
||||||
<div class="card-body">
|
<div className="card border-secondary mb-3">
|
||||||
<h5>{i18n.t("subscribed")}</h5>
|
<div className="card-body">
|
||||||
<ul class="list-unstyled mb-0">
|
<h5>{i18n.t("subscribed")}</h5>
|
||||||
{follows.map(cfv => (
|
<ul className="list-unstyled mb-0">
|
||||||
<li>
|
{follows.map(cfv => (
|
||||||
<CommunityLink community={cfv.community} />
|
<li key={cfv.community.id}>
|
||||||
</li>
|
<CommunityLink community={cfv.community} />
|
||||||
))}
|
</li>
|
||||||
</ul>
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
);
|
||||||
|
} else {
|
||||||
|
return <></>;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
none: void 0,
|
none: void 0,
|
||||||
|
@ -715,8 +730,7 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
this.props.history.push(
|
this.props.history.push(
|
||||||
`${typeView}/view/${viewStr}/sort/${sortStr}/page/${page}`
|
`${typeView}/view/${viewStr}/sort/${sortStr}/page/${page}`
|
||||||
);
|
);
|
||||||
this.state.loading = true;
|
this.setState({ loading: true });
|
||||||
this.setState(this.state);
|
|
||||||
this.fetchUserData();
|
this.fetchUserData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -736,29 +750,24 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanShow(i: Profile) {
|
handleModBanShow(i: Profile) {
|
||||||
i.state.showBanDialog = true;
|
i.setState({ showBanDialog: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanReasonChange(i: Profile, event: any) {
|
handleModBanReasonChange(i: Profile, event: any) {
|
||||||
i.state.banReason = event.target.value;
|
i.setState({ banReason: event.target.value });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanExpireDaysChange(i: Profile, event: any) {
|
handleModBanExpireDaysChange(i: Profile, event: any) {
|
||||||
i.state.banExpireDays = event.target.value;
|
i.setState({ banExpireDays: event.target.value });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveDataChange(i: Profile, event: any) {
|
handleModRemoveDataChange(i: Profile, event: any) {
|
||||||
i.state.removeData = event.target.checked;
|
i.setState({ removeData: event.target.checked });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanSubmitCancel(i: Profile, event?: any) {
|
handleModBanSubmitCancel(i: Profile, event?: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.showBanDialog = false;
|
i.setState({ showBanDialog: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanSubmit(i: Profile, event?: any) {
|
handleModBanSubmit(i: Profile, event?: any) {
|
||||||
|
@ -771,7 +780,7 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
// If its an unban, restore all their data
|
// If its an unban, restore all their data
|
||||||
let ban = !person.banned;
|
let ban = !person.banned;
|
||||||
if (ban == false) {
|
if (ban == false) {
|
||||||
i.state.removeData = false;
|
i.setState({ removeData: false });
|
||||||
}
|
}
|
||||||
let form = new BanPerson({
|
let form = new BanPerson({
|
||||||
person_id: person.id,
|
person_id: person.id,
|
||||||
|
@ -783,8 +792,7 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.banPerson(form));
|
WebSocketService.Instance.send(wsClient.banPerson(form));
|
||||||
|
|
||||||
i.state.showBanDialog = false;
|
i.setState({ showBanDialog: false });
|
||||||
i.setState(i.state);
|
|
||||||
},
|
},
|
||||||
none: void 0,
|
none: void 0,
|
||||||
});
|
});
|
||||||
|
@ -809,15 +817,12 @@ export class Profile extends Component<any, ProfileState> {
|
||||||
msg,
|
msg,
|
||||||
GetPersonDetailsResponse
|
GetPersonDetailsResponse
|
||||||
);
|
);
|
||||||
this.state.personRes = Some(data);
|
this.setState({ personRes: Some(data), loading: false });
|
||||||
this.state.loading = false;
|
|
||||||
this.setPersonBlock();
|
this.setPersonBlock();
|
||||||
this.setState(this.state);
|
|
||||||
restoreScrollPosition(this.context);
|
restoreScrollPosition(this.context);
|
||||||
} else if (op == UserOperation.AddAdmin) {
|
} else if (op == UserOperation.AddAdmin) {
|
||||||
let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
|
let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
|
||||||
this.state.siteRes.admins = data.admins;
|
this.setState(s => ((s.siteRes.admins = data.admins), s));
|
||||||
this.setState(this.state);
|
|
||||||
} else if (op == UserOperation.CreateCommentLike) {
|
} else if (op == UserOperation.CreateCommentLike) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
createCommentLikeRes(
|
createCommentLikeRes(
|
||||||
|
|
|
@ -75,10 +75,13 @@ 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.listRegistrationApplicationsResponse = Some(
|
this.state = {
|
||||||
this.isoData.routeData[0] as ListRegistrationApplicationsResponse
|
...this.state,
|
||||||
);
|
listRegistrationApplicationsResponse: Some(
|
||||||
this.state.loading = false;
|
this.isoData.routeData[0] as ListRegistrationApplicationsResponse
|
||||||
|
),
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this.refetch();
|
this.refetch();
|
||||||
}
|
}
|
||||||
|
@ -110,21 +113,21 @@ export class RegistrationApplications extends Component<
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<h5>
|
<h5>
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
</h5>
|
</h5>
|
||||||
) : (
|
) : (
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
description={None}
|
||||||
image={None}
|
image={None}
|
||||||
/>
|
/>
|
||||||
<h5 class="mb-2">{i18n.t("registration_applications")}</h5>
|
<h5 className="mb-2">{i18n.t("registration_applications")}</h5>
|
||||||
{this.selects()}
|
{this.selects()}
|
||||||
{this.applicationList()}
|
{this.applicationList()}
|
||||||
<Paginator
|
<Paginator
|
||||||
|
@ -140,7 +143,7 @@ export class RegistrationApplications extends Component<
|
||||||
|
|
||||||
unreadOrAllRadios() {
|
unreadOrAllRadios() {
|
||||||
return (
|
return (
|
||||||
<div class="btn-group btn-group-toggle flex-wrap mb-2">
|
<div className="btn-group btn-group-toggle flex-wrap mb-2">
|
||||||
<label
|
<label
|
||||||
className={`btn btn-outline-secondary pointer
|
className={`btn btn-outline-secondary pointer
|
||||||
${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
|
${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
|
||||||
|
@ -174,7 +177,7 @@ export class RegistrationApplications extends Component<
|
||||||
selects() {
|
selects() {
|
||||||
return (
|
return (
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<span class="mr-3">{this.unreadOrAllRadios()}</span>
|
<span className="mr-3">{this.unreadOrAllRadios()}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -199,9 +202,7 @@ export class RegistrationApplications extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnreadOrAllChange(i: RegistrationApplications, event: any) {
|
handleUnreadOrAllChange(i: RegistrationApplications, event: any) {
|
||||||
i.state.unreadOrAll = Number(event.target.value);
|
i.setState({ unreadOrAll: Number(event.target.value), page: 1 });
|
||||||
i.state.page = 1;
|
|
||||||
i.setState(i.state);
|
|
||||||
i.refetch();
|
i.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,10 +249,11 @@ export class RegistrationApplications extends Component<
|
||||||
msg,
|
msg,
|
||||||
ListRegistrationApplicationsResponse
|
ListRegistrationApplicationsResponse
|
||||||
);
|
);
|
||||||
this.state.listRegistrationApplicationsResponse = Some(data);
|
this.setState({
|
||||||
this.state.loading = false;
|
listRegistrationApplicationsResponse: Some(data),
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.setState(this.state);
|
|
||||||
} else if (op == UserOperation.ApproveRegistrationApplication) {
|
} else if (op == UserOperation.ApproveRegistrationApplication) {
|
||||||
let data = wsJsonToRes<RegistrationApplicationResponse>(
|
let data = wsJsonToRes<RegistrationApplicationResponse>(
|
||||||
msg,
|
msg,
|
||||||
|
|
|
@ -8,8 +8,12 @@ import {
|
||||||
ListCommentReportsResponse,
|
ListCommentReportsResponse,
|
||||||
ListPostReports,
|
ListPostReports,
|
||||||
ListPostReportsResponse,
|
ListPostReportsResponse,
|
||||||
|
ListPrivateMessageReports,
|
||||||
|
ListPrivateMessageReportsResponse,
|
||||||
PostReportResponse,
|
PostReportResponse,
|
||||||
PostReportView,
|
PostReportView,
|
||||||
|
PrivateMessageReportResponse,
|
||||||
|
PrivateMessageReportView,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
wsUserOp,
|
wsUserOp,
|
||||||
|
@ -19,6 +23,7 @@ import { i18n } from "../../i18next";
|
||||||
import { InitialFetchRequest } from "../../interfaces";
|
import { InitialFetchRequest } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
|
amAdmin,
|
||||||
auth,
|
auth,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
|
@ -27,6 +32,7 @@ import {
|
||||||
toast,
|
toast,
|
||||||
updateCommentReportRes,
|
updateCommentReportRes,
|
||||||
updatePostReportRes,
|
updatePostReportRes,
|
||||||
|
updatePrivateMessageReportRes,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
|
@ -35,6 +41,7 @@ import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
import { Paginator } from "../common/paginator";
|
import { Paginator } from "../common/paginator";
|
||||||
import { PostReport } from "../post/post-report";
|
import { PostReport } from "../post/post-report";
|
||||||
|
import { PrivateMessageReport } from "../private_message/private-message-report";
|
||||||
|
|
||||||
enum UnreadOrAll {
|
enum UnreadOrAll {
|
||||||
Unread,
|
Unread,
|
||||||
|
@ -45,23 +52,26 @@ enum MessageType {
|
||||||
All,
|
All,
|
||||||
CommentReport,
|
CommentReport,
|
||||||
PostReport,
|
PostReport,
|
||||||
|
PrivateMessageReport,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MessageEnum {
|
enum MessageEnum {
|
||||||
CommentReport,
|
CommentReport,
|
||||||
PostReport,
|
PostReport,
|
||||||
|
PrivateMessageReport,
|
||||||
}
|
}
|
||||||
|
|
||||||
type ItemType = {
|
type ItemType = {
|
||||||
id: number;
|
id: number;
|
||||||
type_: MessageEnum;
|
type_: MessageEnum;
|
||||||
view: CommentReportView | PostReportView;
|
view: CommentReportView | PostReportView | PrivateMessageReportView;
|
||||||
published: string;
|
published: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ReportsState {
|
interface ReportsState {
|
||||||
listCommentReportsResponse: Option<ListCommentReportsResponse>;
|
listCommentReportsResponse: Option<ListCommentReportsResponse>;
|
||||||
listPostReportsResponse: Option<ListPostReportsResponse>;
|
listPostReportsResponse: Option<ListPostReportsResponse>;
|
||||||
|
listPrivateMessageReportsResponse: Option<ListPrivateMessageReportsResponse>;
|
||||||
unreadOrAll: UnreadOrAll;
|
unreadOrAll: UnreadOrAll;
|
||||||
messageType: MessageType;
|
messageType: MessageType;
|
||||||
combined: ItemType[];
|
combined: ItemType[];
|
||||||
|
@ -74,12 +84,14 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
private isoData = setIsoData(
|
private isoData = setIsoData(
|
||||||
this.context,
|
this.context,
|
||||||
ListCommentReportsResponse,
|
ListCommentReportsResponse,
|
||||||
ListPostReportsResponse
|
ListPostReportsResponse,
|
||||||
|
ListPrivateMessageReportsResponse
|
||||||
);
|
);
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: ReportsState = {
|
private emptyState: ReportsState = {
|
||||||
listCommentReportsResponse: None,
|
listCommentReportsResponse: None,
|
||||||
listPostReportsResponse: None,
|
listPostReportsResponse: None,
|
||||||
|
listPrivateMessageReportsResponse: None,
|
||||||
unreadOrAll: UnreadOrAll.Unread,
|
unreadOrAll: UnreadOrAll.Unread,
|
||||||
messageType: MessageType.All,
|
messageType: MessageType.All,
|
||||||
combined: [],
|
combined: [],
|
||||||
|
@ -104,14 +116,28 @@ 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.listCommentReportsResponse = Some(
|
this.state = {
|
||||||
this.isoData.routeData[0] as ListCommentReportsResponse
|
...this.state,
|
||||||
);
|
listCommentReportsResponse: Some(
|
||||||
this.state.listPostReportsResponse = Some(
|
this.isoData.routeData[0] as ListCommentReportsResponse
|
||||||
this.isoData.routeData[1] as ListPostReportsResponse
|
),
|
||||||
);
|
listPostReportsResponse: Some(
|
||||||
this.state.combined = this.buildCombined();
|
this.isoData.routeData[1] as ListPostReportsResponse
|
||||||
this.state.loading = false;
|
),
|
||||||
|
};
|
||||||
|
if (amAdmin()) {
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
listPrivateMessageReportsResponse: Some(
|
||||||
|
this.isoData.routeData[2] as ListPrivateMessageReportsResponse
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
combined: this.buildCombined(),
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this.refetch();
|
this.refetch();
|
||||||
}
|
}
|
||||||
|
@ -139,27 +165,29 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<h5>
|
<h5>
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
</h5>
|
</h5>
|
||||||
) : (
|
) : (
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
description={None}
|
||||||
image={None}
|
image={None}
|
||||||
/>
|
/>
|
||||||
<h5 class="mb-2">{i18n.t("reports")}</h5>
|
<h5 className="mb-2">{i18n.t("reports")}</h5>
|
||||||
{this.selects()}
|
{this.selects()}
|
||||||
{this.state.messageType == MessageType.All && this.all()}
|
{this.state.messageType == MessageType.All && this.all()}
|
||||||
{this.state.messageType == MessageType.CommentReport &&
|
{this.state.messageType == MessageType.CommentReport &&
|
||||||
this.commentReports()}
|
this.commentReports()}
|
||||||
{this.state.messageType == MessageType.PostReport &&
|
{this.state.messageType == MessageType.PostReport &&
|
||||||
this.postReports()}
|
this.postReports()}
|
||||||
|
{this.state.messageType == MessageType.PrivateMessageReport &&
|
||||||
|
this.privateMessageReports()}
|
||||||
<Paginator
|
<Paginator
|
||||||
page={this.state.page}
|
page={this.state.page}
|
||||||
onChange={this.handlePageChange}
|
onChange={this.handlePageChange}
|
||||||
|
@ -173,7 +201,7 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
|
|
||||||
unreadOrAllRadios() {
|
unreadOrAllRadios() {
|
||||||
return (
|
return (
|
||||||
<div class="btn-group btn-group-toggle flex-wrap mb-2">
|
<div className="btn-group btn-group-toggle flex-wrap mb-2">
|
||||||
<label
|
<label
|
||||||
className={`btn btn-outline-secondary pointer
|
className={`btn btn-outline-secondary pointer
|
||||||
${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
|
${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
|
||||||
|
@ -206,7 +234,7 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
|
|
||||||
messageTypeRadios() {
|
messageTypeRadios() {
|
||||||
return (
|
return (
|
||||||
<div class="btn-group btn-group-toggle flex-wrap mb-2">
|
<div className="btn-group btn-group-toggle flex-wrap mb-2">
|
||||||
<label
|
<label
|
||||||
className={`btn btn-outline-secondary pointer
|
className={`btn btn-outline-secondary pointer
|
||||||
${this.state.messageType == MessageType.All && "active"}
|
${this.state.messageType == MessageType.All && "active"}
|
||||||
|
@ -246,6 +274,26 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
/>
|
/>
|
||||||
{i18n.t("posts")}
|
{i18n.t("posts")}
|
||||||
</label>
|
</label>
|
||||||
|
{amAdmin() && (
|
||||||
|
<label
|
||||||
|
className={`btn btn-outline-secondary pointer
|
||||||
|
${
|
||||||
|
this.state.messageType == MessageType.PrivateMessageReport &&
|
||||||
|
"active"
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
value={MessageType.PrivateMessageReport}
|
||||||
|
checked={
|
||||||
|
this.state.messageType == MessageType.PrivateMessageReport
|
||||||
|
}
|
||||||
|
onChange={linkEvent(this, this.handleMessageTypeChange)}
|
||||||
|
/>
|
||||||
|
{i18n.t("messages")}
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -253,13 +301,13 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
selects() {
|
selects() {
|
||||||
return (
|
return (
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<span class="mr-3">{this.unreadOrAllRadios()}</span>
|
<span className="mr-3">{this.unreadOrAllRadios()}</span>
|
||||||
<span class="mr-3">{this.messageTypeRadios()}</span>
|
<span className="mr-3">{this.messageTypeRadios()}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
replyToReplyType(r: CommentReportView): ItemType {
|
commentReportToItemType(r: CommentReportView): ItemType {
|
||||||
return {
|
return {
|
||||||
id: r.comment_report.id,
|
id: r.comment_report.id,
|
||||||
type_: MessageEnum.CommentReport,
|
type_: MessageEnum.CommentReport,
|
||||||
|
@ -268,7 +316,7 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
mentionToReplyType(r: PostReportView): ItemType {
|
postReportToItemType(r: PostReportView): ItemType {
|
||||||
return {
|
return {
|
||||||
id: r.post_report.id,
|
id: r.post_report.id,
|
||||||
type_: MessageEnum.PostReport,
|
type_: MessageEnum.PostReport,
|
||||||
|
@ -277,17 +325,31 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
privateMessageReportToItemType(r: PrivateMessageReportView): ItemType {
|
||||||
|
return {
|
||||||
|
id: r.private_message_report.id,
|
||||||
|
type_: MessageEnum.PrivateMessageReport,
|
||||||
|
view: r,
|
||||||
|
published: r.private_message_report.published,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
buildCombined(): ItemType[] {
|
buildCombined(): ItemType[] {
|
||||||
let comments: ItemType[] = this.state.listCommentReportsResponse
|
let comments: ItemType[] = this.state.listCommentReportsResponse
|
||||||
.map(r => r.comment_reports)
|
.map(r => r.comment_reports)
|
||||||
.unwrapOr([])
|
.unwrapOr([])
|
||||||
.map(r => this.replyToReplyType(r));
|
.map(r => this.commentReportToItemType(r));
|
||||||
let posts: ItemType[] = this.state.listPostReportsResponse
|
let posts: ItemType[] = this.state.listPostReportsResponse
|
||||||
.map(r => r.post_reports)
|
.map(r => r.post_reports)
|
||||||
.unwrapOr([])
|
.unwrapOr([])
|
||||||
.map(r => this.mentionToReplyType(r));
|
.map(r => this.postReportToItemType(r));
|
||||||
|
let privateMessages: ItemType[] =
|
||||||
|
this.state.listPrivateMessageReportsResponse
|
||||||
|
.map(r => r.private_message_reports)
|
||||||
|
.unwrapOr([])
|
||||||
|
.map(r => this.privateMessageReportToItemType(r));
|
||||||
|
|
||||||
return [...comments, ...posts].sort((a, b) =>
|
return [...comments, ...posts, ...privateMessages].sort((a, b) =>
|
||||||
b.published.localeCompare(a.published)
|
b.published.localeCompare(a.published)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -300,6 +362,13 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
);
|
);
|
||||||
case MessageEnum.PostReport:
|
case MessageEnum.PostReport:
|
||||||
return <PostReport key={i.id} report={i.view as PostReportView} />;
|
return <PostReport key={i.id} report={i.view as PostReportView} />;
|
||||||
|
case MessageEnum.PrivateMessageReport:
|
||||||
|
return (
|
||||||
|
<PrivateMessageReport
|
||||||
|
key={i.id}
|
||||||
|
report={i.view as PrivateMessageReportView}
|
||||||
|
/>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return <div />;
|
return <div />;
|
||||||
}
|
}
|
||||||
|
@ -350,22 +419,37 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
privateMessageReports() {
|
||||||
|
return this.state.listPrivateMessageReportsResponse.match({
|
||||||
|
some: res => (
|
||||||
|
<div>
|
||||||
|
{res.private_message_reports.map(pmr => (
|
||||||
|
<>
|
||||||
|
<hr />
|
||||||
|
<PrivateMessageReport
|
||||||
|
key={pmr.private_message_report.id}
|
||||||
|
report={pmr}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
handlePageChange(page: number) {
|
handlePageChange(page: number) {
|
||||||
this.setState({ page });
|
this.setState({ page });
|
||||||
this.refetch();
|
this.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnreadOrAllChange(i: Reports, event: any) {
|
handleUnreadOrAllChange(i: Reports, event: any) {
|
||||||
i.state.unreadOrAll = Number(event.target.value);
|
i.setState({ unreadOrAll: Number(event.target.value), page: 1 });
|
||||||
i.state.page = 1;
|
|
||||||
i.setState(i.state);
|
|
||||||
i.refetch();
|
i.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMessageTypeChange(i: Reports, event: any) {
|
handleMessageTypeChange(i: Reports, event: any) {
|
||||||
i.state.messageType = Number(event.target.value);
|
i.setState({ messageType: Number(event.target.value), page: 1 });
|
||||||
i.state.page = 1;
|
|
||||||
i.setState(i.state);
|
|
||||||
i.refetch();
|
i.refetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,6 +482,18 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
});
|
});
|
||||||
promises.push(req.client.listPostReports(postReportsForm));
|
promises.push(req.client.listPostReports(postReportsForm));
|
||||||
|
|
||||||
|
if (amAdmin()) {
|
||||||
|
let privateMessageReportsForm = new ListPrivateMessageReports({
|
||||||
|
unresolved_only,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
auth,
|
||||||
|
});
|
||||||
|
promises.push(
|
||||||
|
req.client.listPrivateMessageReports(privateMessageReportsForm)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return promises;
|
return promises;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,6 +524,18 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
auth: auth().unwrap(),
|
auth: auth().unwrap(),
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.listPostReports(postReportsForm));
|
WebSocketService.Instance.send(wsClient.listPostReports(postReportsForm));
|
||||||
|
|
||||||
|
if (amAdmin()) {
|
||||||
|
let privateMessageReportsForm = new ListPrivateMessageReports({
|
||||||
|
unresolved_only,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
auth: auth().unwrap(),
|
||||||
|
});
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.listPrivateMessageReports(privateMessageReportsForm)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -443,24 +551,30 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
msg,
|
msg,
|
||||||
ListCommentReportsResponse
|
ListCommentReportsResponse
|
||||||
);
|
);
|
||||||
this.state.listCommentReportsResponse = Some(data);
|
this.setState({ listCommentReportsResponse: Some(data) });
|
||||||
this.state.combined = this.buildCombined();
|
this.setState({ combined: this.buildCombined(), loading: false });
|
||||||
this.state.loading = false;
|
|
||||||
// this.sendUnreadCount();
|
// this.sendUnreadCount();
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.setState(this.state);
|
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.ListPostReports) {
|
} else if (op == UserOperation.ListPostReports) {
|
||||||
let data = wsJsonToRes<ListPostReportsResponse>(
|
let data = wsJsonToRes<ListPostReportsResponse>(
|
||||||
msg,
|
msg,
|
||||||
ListPostReportsResponse
|
ListPostReportsResponse
|
||||||
);
|
);
|
||||||
this.state.listPostReportsResponse = Some(data);
|
this.setState({ listPostReportsResponse: Some(data) });
|
||||||
this.state.combined = this.buildCombined();
|
this.setState({ combined: this.buildCombined(), loading: false });
|
||||||
this.state.loading = false;
|
// this.sendUnreadCount();
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
setupTippy();
|
||||||
|
} else if (op == UserOperation.ListPrivateMessageReports) {
|
||||||
|
let data = wsJsonToRes<ListPrivateMessageReportsResponse>(
|
||||||
|
msg,
|
||||||
|
ListPrivateMessageReportsResponse
|
||||||
|
);
|
||||||
|
this.setState({ listPrivateMessageReportsResponse: Some(data) });
|
||||||
|
this.setState({ combined: this.buildCombined(), loading: false });
|
||||||
// this.sendUnreadCount();
|
// this.sendUnreadCount();
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.setState(this.state);
|
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.ResolvePostReport) {
|
} else if (op == UserOperation.ResolvePostReport) {
|
||||||
let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
|
let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
|
||||||
|
@ -490,6 +604,24 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
urcs.next(urcs.getValue() + 1);
|
urcs.next(urcs.getValue() + 1);
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
} else if (op == UserOperation.ResolvePrivateMessageReport) {
|
||||||
|
let data = wsJsonToRes<PrivateMessageReportResponse>(
|
||||||
|
msg,
|
||||||
|
PrivateMessageReportResponse
|
||||||
|
);
|
||||||
|
updatePrivateMessageReportRes(
|
||||||
|
data.private_message_report_view,
|
||||||
|
this.state.listPrivateMessageReportsResponse
|
||||||
|
.map(r => r.private_message_reports)
|
||||||
|
.unwrapOr([])
|
||||||
|
);
|
||||||
|
let urcs = UserService.Instance.unreadReportCountSub;
|
||||||
|
if (data.private_message_report_view.private_message_report.resolved) {
|
||||||
|
urcs.next(urcs.getValue() - 1);
|
||||||
|
} else {
|
||||||
|
urcs.next(urcs.getValue() + 1);
|
||||||
|
}
|
||||||
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ import {
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
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 { LanguageSelect } from "../common/language-select";
|
||||||
import { ListingTypeSelect } from "../common/listing-type-select";
|
import { ListingTypeSelect } from "../common/listing-type-select";
|
||||||
import { MarkdownTextArea } from "../common/markdown-textarea";
|
import { MarkdownTextArea } from "../common/markdown-textarea";
|
||||||
import { SortSelect } from "../common/sort-select";
|
import { SortSelect } from "../common/sort-select";
|
||||||
|
@ -99,7 +100,8 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
default_sort_type: None,
|
default_sort_type: None,
|
||||||
default_listing_type: None,
|
default_listing_type: None,
|
||||||
theme: None,
|
theme: None,
|
||||||
lang: None,
|
interface_language: None,
|
||||||
|
discussion_languages: None,
|
||||||
avatar: None,
|
avatar: None,
|
||||||
banner: None,
|
banner: None,
|
||||||
display_name: None,
|
display_name: None,
|
||||||
|
@ -140,6 +142,8 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
this.handleSortTypeChange = this.handleSortTypeChange.bind(this);
|
this.handleSortTypeChange = this.handleSortTypeChange.bind(this);
|
||||||
this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
|
this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
|
||||||
this.handleBioChange = this.handleBioChange.bind(this);
|
this.handleBioChange = this.handleBioChange.bind(this);
|
||||||
|
this.handleDiscussionLanguageChange =
|
||||||
|
this.handleDiscussionLanguageChange.bind(this);
|
||||||
|
|
||||||
this.handleAvatarUpload = this.handleAvatarUpload.bind(this);
|
this.handleAvatarUpload = this.handleAvatarUpload.bind(this);
|
||||||
this.handleAvatarRemove = this.handleAvatarRemove.bind(this);
|
this.handleAvatarRemove = this.handleAvatarRemove.bind(this);
|
||||||
|
@ -150,13 +154,44 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
this.setUserInfo();
|
if (UserService.Instance.myUserInfo.isSome()) {
|
||||||
|
let mui = UserService.Instance.myUserInfo.unwrap();
|
||||||
|
let luv = mui.local_user_view;
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
personBlocks: mui.person_blocks,
|
||||||
|
communityBlocks: mui.community_blocks,
|
||||||
|
saveUserSettingsForm: {
|
||||||
|
...this.state.saveUserSettingsForm,
|
||||||
|
show_nsfw: Some(luv.local_user.show_nsfw),
|
||||||
|
theme: Some(luv.local_user.theme ? luv.local_user.theme : "browser"),
|
||||||
|
default_sort_type: Some(luv.local_user.default_sort_type),
|
||||||
|
default_listing_type: Some(luv.local_user.default_listing_type),
|
||||||
|
interface_language: Some(luv.local_user.interface_language),
|
||||||
|
discussion_languages: Some(mui.discussion_languages.map(l => l.id)),
|
||||||
|
avatar: luv.person.avatar,
|
||||||
|
banner: luv.person.banner,
|
||||||
|
display_name: luv.person.display_name,
|
||||||
|
show_avatars: Some(luv.local_user.show_avatars),
|
||||||
|
bot_account: Some(luv.person.bot_account),
|
||||||
|
show_bot_accounts: Some(luv.local_user.show_bot_accounts),
|
||||||
|
show_scores: Some(luv.local_user.show_scores),
|
||||||
|
show_read_posts: Some(luv.local_user.show_read_posts),
|
||||||
|
show_new_post_notifs: Some(luv.local_user.show_new_post_notifs),
|
||||||
|
email: luv.local_user.email,
|
||||||
|
bio: luv.person.bio,
|
||||||
|
send_notifications_to_email: Some(
|
||||||
|
luv.local_user.send_notifications_to_email
|
||||||
|
),
|
||||||
|
matrix_user_id: luv.person.matrix_user_id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
setupTippy();
|
setupTippy();
|
||||||
this.state.themeList = await fetchThemeList();
|
this.setState({ themeList: await fetchThemeList() });
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -169,7 +204,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
<>
|
<>
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
|
@ -177,10 +212,10 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
description={Some(this.documentTitle)}
|
description={Some(this.documentTitle)}
|
||||||
image={this.state.saveUserSettingsForm.avatar}
|
image={this.state.saveUserSettingsForm.avatar}
|
||||||
/>
|
/>
|
||||||
<ul class="nav nav-tabs mb-2">
|
<ul className="nav nav-tabs mb-2">
|
||||||
<li class="nav-item">
|
<li className="nav-item">
|
||||||
<button
|
<button
|
||||||
class={`nav-link btn ${
|
className={`nav-link btn ${
|
||||||
this.state.currentTab == "settings" && "active"
|
this.state.currentTab == "settings" && "active"
|
||||||
}`}
|
}`}
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
|
@ -191,9 +226,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
{i18n.t("settings")}
|
{i18n.t("settings")}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li className="nav-item">
|
||||||
<button
|
<button
|
||||||
class={`nav-link btn ${
|
className={`nav-link btn ${
|
||||||
this.state.currentTab == "blocks" && "active"
|
this.state.currentTab == "blocks" && "active"
|
||||||
}`}
|
}`}
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
|
@ -214,15 +249,15 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
|
|
||||||
userSettings() {
|
userSettings() {
|
||||||
return (
|
return (
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12 col-md-6">
|
<div className="col-12 col-md-6">
|
||||||
<div class="card border-secondary mb-3">
|
<div className="card border-secondary mb-3">
|
||||||
<div class="card-body">{this.saveUserSettingsHtmlForm()}</div>
|
<div className="card-body">{this.saveUserSettingsHtmlForm()}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-6">
|
<div className="col-12 col-md-6">
|
||||||
<div class="card border-secondary mb-3">
|
<div className="card border-secondary mb-3">
|
||||||
<div class="card-body">{this.changePasswordHtmlForm()}</div>
|
<div className="card-body">{this.changePasswordHtmlForm()}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -231,15 +266,15 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
|
|
||||||
blockCards() {
|
blockCards() {
|
||||||
return (
|
return (
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12 col-md-6">
|
<div className="col-12 col-md-6">
|
||||||
<div class="card border-secondary mb-3">
|
<div className="card border-secondary mb-3">
|
||||||
<div class="card-body">{this.blockUserCard()}</div>
|
<div className="card-body">{this.blockUserCard()}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-6">
|
<div className="col-12 col-md-6">
|
||||||
<div class="card border-secondary mb-3">
|
<div className="card border-secondary mb-3">
|
||||||
<div class="card-body">{this.blockCommunityCard()}</div>
|
<div className="card-body">{this.blockCommunityCard()}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -251,15 +286,15 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
<>
|
<>
|
||||||
<h5>{i18n.t("change_password")}</h5>
|
<h5>{i18n.t("change_password")}</h5>
|
||||||
<form onSubmit={linkEvent(this, this.handleChangePasswordSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleChangePasswordSubmit)}>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-5 col-form-label" htmlFor="user-password">
|
<label className="col-sm-5 col-form-label" htmlFor="user-password">
|
||||||
{i18n.t("new_password")}
|
{i18n.t("new_password")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-7">
|
<div className="col-sm-7">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="user-password"
|
id="user-password"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
value={this.state.changePasswordForm.new_password}
|
value={this.state.changePasswordForm.new_password}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
maxLength={60}
|
maxLength={60}
|
||||||
|
@ -267,18 +302,18 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label
|
<label
|
||||||
class="col-sm-5 col-form-label"
|
className="col-sm-5 col-form-label"
|
||||||
htmlFor="user-verify-password"
|
htmlFor="user-verify-password"
|
||||||
>
|
>
|
||||||
{i18n.t("verify_password")}
|
{i18n.t("verify_password")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-7">
|
<div className="col-sm-7">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="user-verify-password"
|
id="user-verify-password"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
value={this.state.changePasswordForm.new_password_verify}
|
value={this.state.changePasswordForm.new_password_verify}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
maxLength={60}
|
maxLength={60}
|
||||||
|
@ -286,15 +321,18 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-5 col-form-label" htmlFor="user-old-password">
|
<label
|
||||||
|
className="col-sm-5 col-form-label"
|
||||||
|
htmlFor="user-old-password"
|
||||||
|
>
|
||||||
{i18n.t("old_password")}
|
{i18n.t("old_password")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-7">
|
<div className="col-sm-7">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="user-old-password"
|
id="user-old-password"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
value={this.state.changePasswordForm.old_password}
|
value={this.state.changePasswordForm.old_password}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
maxLength={60}
|
maxLength={60}
|
||||||
|
@ -302,8 +340,8 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<button type="submit" class="btn btn-block btn-secondary mr-4">
|
<button type="submit" className="btn btn-block btn-secondary mr-4">
|
||||||
{this.state.changePasswordLoading ? (
|
{this.state.changePasswordLoading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : (
|
) : (
|
||||||
|
@ -329,9 +367,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h5>{i18n.t("blocked_users")}</h5>
|
<h5>{i18n.t("blocked_users")}</h5>
|
||||||
<ul class="list-unstyled mb-0">
|
<ul className="list-unstyled mb-0">
|
||||||
{this.state.personBlocks.map(pb => (
|
{this.state.personBlocks.map(pb => (
|
||||||
<li>
|
<li key={pb.target.id}>
|
||||||
<span>
|
<span>
|
||||||
<PersonListing person={pb.target} />
|
<PersonListing person={pb.target} />
|
||||||
<button
|
<button
|
||||||
|
@ -354,13 +392,16 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
|
|
||||||
blockUserForm() {
|
blockUserForm() {
|
||||||
return (
|
return (
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-md-4 col-form-label" htmlFor="block-person-filter">
|
<label
|
||||||
|
className="col-md-4 col-form-label"
|
||||||
|
htmlFor="block-person-filter"
|
||||||
|
>
|
||||||
{i18n.t("block_user")}
|
{i18n.t("block_user")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-md-8">
|
<div className="col-md-8">
|
||||||
<select
|
<select
|
||||||
class="form-control"
|
className="form-control"
|
||||||
id="block-person-filter"
|
id="block-person-filter"
|
||||||
value={this.state.blockPerson.map(p => p.person.id).unwrapOr(0)}
|
value={this.state.blockPerson.map(p => p.person.id).unwrapOr(0)}
|
||||||
>
|
>
|
||||||
|
@ -392,9 +433,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h5>{i18n.t("blocked_communities")}</h5>
|
<h5>{i18n.t("blocked_communities")}</h5>
|
||||||
<ul class="list-unstyled mb-0">
|
<ul className="list-unstyled mb-0">
|
||||||
{this.state.communityBlocks.map(cb => (
|
{this.state.communityBlocks.map(cb => (
|
||||||
<li>
|
<li key={cb.community.id}>
|
||||||
<span>
|
<span>
|
||||||
<CommunityLink community={cb.community} />
|
<CommunityLink community={cb.community} />
|
||||||
<button
|
<button
|
||||||
|
@ -417,13 +458,16 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
|
|
||||||
blockCommunityForm() {
|
blockCommunityForm() {
|
||||||
return (
|
return (
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-md-4 col-form-label" htmlFor="block-community-filter">
|
<label
|
||||||
|
className="col-md-4 col-form-label"
|
||||||
|
htmlFor="block-community-filter"
|
||||||
|
>
|
||||||
{i18n.t("block_community")}
|
{i18n.t("block_community")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-md-8">
|
<div className="col-md-8">
|
||||||
<select
|
<select
|
||||||
class="form-control"
|
className="form-control"
|
||||||
id="block-community-filter"
|
id="block-community-filter"
|
||||||
value={this.state.blockCommunityId}
|
value={this.state.blockCommunityId}
|
||||||
>
|
>
|
||||||
|
@ -440,19 +484,21 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
saveUserSettingsHtmlForm() {
|
saveUserSettingsHtmlForm() {
|
||||||
|
let selectedLangs = this.state.saveUserSettingsForm.discussion_languages;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h5>{i18n.t("settings")}</h5>
|
<h5>{i18n.t("settings")}</h5>
|
||||||
<form onSubmit={linkEvent(this, this.handleSaveSettingsSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleSaveSettingsSubmit)}>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-5 col-form-label" htmlFor="display-name">
|
<label className="col-sm-5 col-form-label" htmlFor="display-name">
|
||||||
{i18n.t("display_name")}
|
{i18n.t("display_name")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-7">
|
<div className="col-sm-7">
|
||||||
<input
|
<input
|
||||||
id="display-name"
|
id="display-name"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
placeholder={i18n.t("optional")}
|
placeholder={i18n.t("optional")}
|
||||||
value={toUndefined(
|
value={toUndefined(
|
||||||
this.state.saveUserSettingsForm.display_name
|
this.state.saveUserSettingsForm.display_name
|
||||||
|
@ -463,30 +509,32 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-3 col-form-label" htmlFor="user-bio">
|
<label className="col-sm-3 col-form-label" htmlFor="user-bio">
|
||||||
{i18n.t("bio")}
|
{i18n.t("bio")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-9">
|
<div className="col-sm-9">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.saveUserSettingsForm.bio}
|
initialContent={this.state.saveUserSettingsForm.bio}
|
||||||
|
initialLanguageId={None}
|
||||||
onContentChange={this.handleBioChange}
|
onContentChange={this.handleBioChange}
|
||||||
maxLength={Some(300)}
|
maxLength={Some(300)}
|
||||||
placeholder={None}
|
placeholder={None}
|
||||||
buttonTitle={None}
|
buttonTitle={None}
|
||||||
hideNavigationWarnings
|
hideNavigationWarnings
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-3 col-form-label" htmlFor="user-email">
|
<label className="col-sm-3 col-form-label" htmlFor="user-email">
|
||||||
{i18n.t("email")}
|
{i18n.t("email")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-9">
|
<div className="col-sm-9">
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
id="user-email"
|
id="user-email"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
placeholder={i18n.t("optional")}
|
placeholder={i18n.t("optional")}
|
||||||
value={toUndefined(this.state.saveUserSettingsForm.email)}
|
value={toUndefined(this.state.saveUserSettingsForm.email)}
|
||||||
onInput={linkEvent(this, this.handleEmailChange)}
|
onInput={linkEvent(this, this.handleEmailChange)}
|
||||||
|
@ -494,17 +542,17 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-5 col-form-label" htmlFor="matrix-user-id">
|
<label className="col-sm-5 col-form-label" htmlFor="matrix-user-id">
|
||||||
<a href={elementUrl} rel={relTags}>
|
<a href={elementUrl} rel={relTags}>
|
||||||
{i18n.t("matrix_user_id")}
|
{i18n.t("matrix_user_id")}
|
||||||
</a>
|
</a>
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-7">
|
<div className="col-sm-7">
|
||||||
<input
|
<input
|
||||||
id="matrix-user-id"
|
id="matrix-user-id"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
placeholder="@user:example.com"
|
placeholder="@user:example.com"
|
||||||
value={toUndefined(
|
value={toUndefined(
|
||||||
this.state.saveUserSettingsForm.matrix_user_id
|
this.state.saveUserSettingsForm.matrix_user_id
|
||||||
|
@ -514,9 +562,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-3">{i18n.t("avatar")}</label>
|
<label className="col-sm-3">{i18n.t("avatar")}</label>
|
||||||
<div class="col-sm-9">
|
<div className="col-sm-9">
|
||||||
<ImageUploadForm
|
<ImageUploadForm
|
||||||
uploadTitle={i18n.t("upload_avatar")}
|
uploadTitle={i18n.t("upload_avatar")}
|
||||||
imageSrc={this.state.saveUserSettingsForm.avatar}
|
imageSrc={this.state.saveUserSettingsForm.avatar}
|
||||||
|
@ -526,9 +574,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-3">{i18n.t("banner")}</label>
|
<label className="col-sm-3">{i18n.t("banner")}</label>
|
||||||
<div class="col-sm-9">
|
<div className="col-sm-9">
|
||||||
<ImageUploadForm
|
<ImageUploadForm
|
||||||
uploadTitle={i18n.t("upload_banner")}
|
uploadTitle={i18n.t("upload_banner")}
|
||||||
imageSrc={this.state.saveUserSettingsForm.banner}
|
imageSrc={this.state.saveUserSettingsForm.banner}
|
||||||
|
@ -537,19 +585,21 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-3" htmlFor="user-language">
|
<label className="col-sm-3" htmlFor="user-language">
|
||||||
{i18n.t("language")}
|
{i18n.t("interface_language")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-9">
|
<div className="col-sm-9">
|
||||||
<select
|
<select
|
||||||
id="user-language"
|
id="user-language"
|
||||||
value={toUndefined(this.state.saveUserSettingsForm.lang)}
|
value={toUndefined(
|
||||||
onChange={linkEvent(this, this.handleLangChange)}
|
this.state.saveUserSettingsForm.interface_language
|
||||||
class="custom-select w-auto"
|
)}
|
||||||
|
onChange={linkEvent(this, this.handleInterfaceLangChange)}
|
||||||
|
className="custom-select w-auto"
|
||||||
>
|
>
|
||||||
<option disabled aria-hidden="true">
|
<option disabled aria-hidden="true">
|
||||||
{i18n.t("language")}
|
{i18n.t("interface_language")}
|
||||||
</option>
|
</option>
|
||||||
<option value="browser">{i18n.t("browser_default")}</option>
|
<option value="browser">{i18n.t("browser_default")}</option>
|
||||||
<option disabled aria-hidden="true">
|
<option disabled aria-hidden="true">
|
||||||
|
@ -558,35 +608,45 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
{languages
|
{languages
|
||||||
.sort((a, b) => a.code.localeCompare(b.code))
|
.sort((a, b) => a.code.localeCompare(b.code))
|
||||||
.map(lang => (
|
.map(lang => (
|
||||||
<option value={lang.code}>{lang.name}</option>
|
<option key={lang.code} value={lang.code}>
|
||||||
|
{lang.name}
|
||||||
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<LanguageSelect
|
||||||
<label class="col-sm-3" htmlFor="user-theme">
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
|
selectedLanguageIds={selectedLangs}
|
||||||
|
multiple={true}
|
||||||
|
onChange={this.handleDiscussionLanguageChange}
|
||||||
|
/>
|
||||||
|
<div className="form-group row">
|
||||||
|
<label className="col-sm-3" htmlFor="user-theme">
|
||||||
{i18n.t("theme")}
|
{i18n.t("theme")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-9">
|
<div className="col-sm-9">
|
||||||
<select
|
<select
|
||||||
id="user-theme"
|
id="user-theme"
|
||||||
value={toUndefined(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"
|
className="custom-select w-auto"
|
||||||
>
|
>
|
||||||
<option disabled aria-hidden="true">
|
<option disabled aria-hidden="true">
|
||||||
{i18n.t("theme")}
|
{i18n.t("theme")}
|
||||||
</option>
|
</option>
|
||||||
<option value="browser">{i18n.t("browser_default")}</option>
|
<option value="browser">{i18n.t("browser_default")}</option>
|
||||||
{this.state.themeList.map(theme => (
|
{this.state.themeList.map(theme => (
|
||||||
<option value={theme}>{theme}</option>
|
<option key={theme} value={theme}>
|
||||||
|
{theme}
|
||||||
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form className="form-group row">
|
<form className="form-group row">
|
||||||
<label class="col-sm-3">{i18n.t("type")}</label>
|
<label className="col-sm-3">{i18n.t("type")}</label>
|
||||||
<div class="col-sm-9">
|
<div className="col-sm-9">
|
||||||
<ListingTypeSelect
|
<ListingTypeSelect
|
||||||
type_={
|
type_={
|
||||||
Object.values(ListingType)[
|
Object.values(ListingType)[
|
||||||
|
@ -602,8 +662,8 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<form className="form-group row">
|
<form className="form-group row">
|
||||||
<label class="col-sm-3">{i18n.t("sort_type")}</label>
|
<label className="col-sm-3">{i18n.t("sort_type")}</label>
|
||||||
<div class="col-sm-9">
|
<div className="col-sm-9">
|
||||||
<SortSelect
|
<SortSelect
|
||||||
sort={
|
sort={
|
||||||
Object.values(SortType)[
|
Object.values(SortType)[
|
||||||
|
@ -617,10 +677,10 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{enableNsfw(this.state.siteRes) && (
|
{enableNsfw(this.state.siteRes) && (
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="user-show-nsfw"
|
id="user-show-nsfw"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={toUndefined(
|
||||||
|
@ -628,16 +688,16 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
)}
|
)}
|
||||||
onChange={linkEvent(this, this.handleShowNsfwChange)}
|
onChange={linkEvent(this, this.handleShowNsfwChange)}
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" htmlFor="user-show-nsfw">
|
<label className="form-check-label" htmlFor="user-show-nsfw">
|
||||||
{i18n.t("show_nsfw")}
|
{i18n.t("show_nsfw")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="user-show-scores"
|
id="user-show-scores"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={toUndefined(
|
||||||
|
@ -645,15 +705,15 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
)}
|
)}
|
||||||
onChange={linkEvent(this, this.handleShowScoresChange)}
|
onChange={linkEvent(this, this.handleShowScoresChange)}
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" htmlFor="user-show-scores">
|
<label className="form-check-label" htmlFor="user-show-scores">
|
||||||
{i18n.t("show_scores")}
|
{i18n.t("show_scores")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="user-show-avatars"
|
id="user-show-avatars"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={toUndefined(
|
||||||
|
@ -661,15 +721,15 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
)}
|
)}
|
||||||
onChange={linkEvent(this, this.handleShowAvatarsChange)}
|
onChange={linkEvent(this, this.handleShowAvatarsChange)}
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" htmlFor="user-show-avatars">
|
<label className="form-check-label" htmlFor="user-show-avatars">
|
||||||
{i18n.t("show_avatars")}
|
{i18n.t("show_avatars")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="user-bot-account"
|
id="user-bot-account"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={toUndefined(
|
||||||
|
@ -677,15 +737,15 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
)}
|
)}
|
||||||
onChange={linkEvent(this, this.handleBotAccount)}
|
onChange={linkEvent(this, this.handleBotAccount)}
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" htmlFor="user-bot-account">
|
<label className="form-check-label" htmlFor="user-bot-account">
|
||||||
{i18n.t("bot_account")}
|
{i18n.t("bot_account")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="user-show-bot-accounts"
|
id="user-show-bot-accounts"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={toUndefined(
|
||||||
|
@ -693,15 +753,18 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
)}
|
)}
|
||||||
onChange={linkEvent(this, this.handleShowBotAccounts)}
|
onChange={linkEvent(this, this.handleShowBotAccounts)}
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" htmlFor="user-show-bot-accounts">
|
<label
|
||||||
|
className="form-check-label"
|
||||||
|
htmlFor="user-show-bot-accounts"
|
||||||
|
>
|
||||||
{i18n.t("show_bot_accounts")}
|
{i18n.t("show_bot_accounts")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="user-show-read-posts"
|
id="user-show-read-posts"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={toUndefined(
|
||||||
|
@ -709,15 +772,18 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
)}
|
)}
|
||||||
onChange={linkEvent(this, this.handleReadPosts)}
|
onChange={linkEvent(this, this.handleReadPosts)}
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" htmlFor="user-show-read-posts">
|
<label
|
||||||
|
className="form-check-label"
|
||||||
|
htmlFor="user-show-read-posts"
|
||||||
|
>
|
||||||
{i18n.t("show_read_posts")}
|
{i18n.t("show_read_posts")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="user-show-new-post-notifs"
|
id="user-show-new-post-notifs"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={toUndefined(
|
||||||
|
@ -726,17 +792,17 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
onChange={linkEvent(this, this.handleShowNewPostNotifs)}
|
onChange={linkEvent(this, this.handleShowNewPostNotifs)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
class="form-check-label"
|
className="form-check-label"
|
||||||
htmlFor="user-show-new-post-notifs"
|
htmlFor="user-show-new-post-notifs"
|
||||||
>
|
>
|
||||||
{i18n.t("show_new_post_notifs")}
|
{i18n.t("show_new_post_notifs")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
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}
|
||||||
|
@ -749,15 +815,15 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
class="form-check-label"
|
className="form-check-label"
|
||||||
htmlFor="user-send-notifications-to-email"
|
htmlFor="user-send-notifications-to-email"
|
||||||
>
|
>
|
||||||
{i18n.t("send_notifications_to_email")}
|
{i18n.t("send_notifications_to_email")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<button type="submit" class="btn btn-block btn-secondary mr-4">
|
<button type="submit" className="btn btn-block btn-secondary mr-4">
|
||||||
{this.state.saveUserSettingsLoading ? (
|
{this.state.saveUserSettingsLoading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : (
|
) : (
|
||||||
|
@ -766,9 +832,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<button
|
<button
|
||||||
class="btn btn-block btn-danger"
|
className="btn btn-block btn-danger"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleDeleteAccountShowConfirmToggle
|
this.handleDeleteAccountShowConfirmToggle
|
||||||
|
@ -778,7 +844,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
</button>
|
</button>
|
||||||
{this.state.deleteAccountShowConfirm && (
|
{this.state.deleteAccountShowConfirm && (
|
||||||
<>
|
<>
|
||||||
<div class="my-2 alert alert-danger" role="alert">
|
<div className="my-2 alert alert-danger" role="alert">
|
||||||
{i18n.t("delete_account_confirm")}
|
{i18n.t("delete_account_confirm")}
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
|
@ -790,10 +856,10 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
this,
|
this,
|
||||||
this.handleDeleteAccountPasswordChange
|
this.handleDeleteAccountPasswordChange
|
||||||
)}
|
)}
|
||||||
class="form-control my-2"
|
className="form-control my-2"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
class="btn btn-danger mr-4"
|
className="btn btn-danger mr-4"
|
||||||
disabled={!this.state.deleteAccountForm.password}
|
disabled={!this.state.deleteAccountForm.password}
|
||||||
onClick={linkEvent(this, this.handleDeleteAccount)}
|
onClick={linkEvent(this, this.handleDeleteAccount)}
|
||||||
>
|
>
|
||||||
|
@ -804,7 +870,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleDeleteAccountShowConfirmToggle
|
this.handleDeleteAccountShowConfirmToggle
|
||||||
|
@ -991,26 +1057,40 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLangChange(i: Settings, event: any) {
|
handleInterfaceLangChange(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.lang = Some(event.target.value);
|
i.state.saveUserSettingsForm.interface_language = Some(event.target.value);
|
||||||
i18n.changeLanguage(
|
i18n.changeLanguage(
|
||||||
getLanguages(i.state.saveUserSettingsForm.lang.unwrap())[0]
|
getLanguages(i.state.saveUserSettingsForm.interface_language.unwrap())[0]
|
||||||
);
|
);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSortTypeChange(val: SortType) {
|
handleDiscussionLanguageChange(val: number[]) {
|
||||||
this.state.saveUserSettingsForm.default_sort_type = Some(
|
this.setState(
|
||||||
Object.keys(SortType).indexOf(val)
|
s => ((s.saveUserSettingsForm.discussion_languages = Some(val)), s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSortTypeChange(val: SortType) {
|
||||||
|
this.setState(
|
||||||
|
s => (
|
||||||
|
(s.saveUserSettingsForm.default_sort_type = Some(
|
||||||
|
Object.keys(SortType).indexOf(val)
|
||||||
|
)),
|
||||||
|
s
|
||||||
|
)
|
||||||
);
|
);
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleListingTypeChange(val: ListingType) {
|
handleListingTypeChange(val: ListingType) {
|
||||||
this.state.saveUserSettingsForm.default_listing_type = Some(
|
this.setState(
|
||||||
Object.keys(ListingType).indexOf(val)
|
s => (
|
||||||
|
(s.saveUserSettingsForm.default_listing_type = Some(
|
||||||
|
Object.keys(ListingType).indexOf(val)
|
||||||
|
)),
|
||||||
|
s
|
||||||
|
)
|
||||||
);
|
);
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEmailChange(i: Settings, event: any) {
|
handleEmailChange(i: Settings, event: any) {
|
||||||
|
@ -1019,28 +1099,23 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBioChange(val: string) {
|
handleBioChange(val: string) {
|
||||||
this.state.saveUserSettingsForm.bio = Some(val);
|
this.setState(s => ((s.saveUserSettingsForm.bio = Some(val)), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAvatarUpload(url: string) {
|
handleAvatarUpload(url: string) {
|
||||||
this.state.saveUserSettingsForm.avatar = Some(url);
|
this.setState(s => ((s.saveUserSettingsForm.avatar = Some(url)), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAvatarRemove() {
|
handleAvatarRemove() {
|
||||||
this.state.saveUserSettingsForm.avatar = Some("");
|
this.setState(s => ((s.saveUserSettingsForm.avatar = Some("")), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerUpload(url: string) {
|
handleBannerUpload(url: string) {
|
||||||
this.state.saveUserSettingsForm.banner = Some(url);
|
this.setState(s => ((s.saveUserSettingsForm.banner = Some(url)), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerRemove() {
|
handleBannerRemove() {
|
||||||
this.state.saveUserSettingsForm.banner = Some("");
|
this.setState(s => ((s.saveUserSettingsForm.banner = Some("")), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDisplayNameChange(i: Settings, event: any) {
|
handleDisplayNameChange(i: Settings, event: any) {
|
||||||
|
@ -1079,30 +1154,26 @@ 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.setState({ saveUserSettingsLoading: true });
|
||||||
i.state.saveUserSettingsForm.auth = auth().unwrap();
|
i.setState(s => ((s.saveUserSettingsForm.auth = auth().unwrap()), s));
|
||||||
i.setState(i.state);
|
|
||||||
|
|
||||||
WebSocketService.Instance.send(
|
let form = new SaveUserSettings({ ...i.state.saveUserSettingsForm });
|
||||||
wsClient.saveUserSettings(i.state.saveUserSettingsForm)
|
WebSocketService.Instance.send(wsClient.saveUserSettings(form));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChangePasswordSubmit(i: Settings, event: any) {
|
handleChangePasswordSubmit(i: Settings, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.changePasswordLoading = true;
|
i.setState({ changePasswordLoading: true });
|
||||||
i.state.changePasswordForm.auth = auth().unwrap();
|
i.setState(s => ((s.changePasswordForm.auth = auth().unwrap()), s));
|
||||||
i.setState(i.state);
|
|
||||||
|
|
||||||
WebSocketService.Instance.send(
|
let form = new ChangePassword({ ...i.state.changePasswordForm });
|
||||||
wsClient.changePassword(i.state.changePasswordForm)
|
|
||||||
);
|
WebSocketService.Instance.send(wsClient.changePassword(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteAccountShowConfirmToggle(i: Settings, event: any) {
|
handleDeleteAccountShowConfirmToggle(i: Settings, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.deleteAccountShowConfirm = !i.state.deleteAccountShowConfirm;
|
i.setState({ deleteAccountShowConfirm: !i.state.deleteAccountShowConfirm });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteAccountPasswordChange(i: Settings, event: any) {
|
handleDeleteAccountPasswordChange(i: Settings, event: any) {
|
||||||
|
@ -1112,13 +1183,12 @@ 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.setState({ deleteAccountLoading: true });
|
||||||
i.state.deleteAccountForm.auth = auth().unwrap();
|
i.setState(s => ((s.deleteAccountForm.auth = auth().unwrap()), s));
|
||||||
i.setState(i.state);
|
|
||||||
|
|
||||||
WebSocketService.Instance.send(
|
let form = new DeleteAccount({ ...i.state.deleteAccountForm });
|
||||||
wsClient.deleteAccount(i.state.deleteAccountForm)
|
|
||||||
);
|
WebSocketService.Instance.send(wsClient.deleteAccount(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSwitchTab(i: { ctx: Settings; tab: string }) {
|
handleSwitchTab(i: { ctx: Settings; tab: string }) {
|
||||||
|
@ -1130,58 +1200,6 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setUserInfo() {
|
|
||||||
UserService.Instance.myUserInfo.match({
|
|
||||||
some: mui => {
|
|
||||||
let luv = mui.local_user_view;
|
|
||||||
this.state.saveUserSettingsForm.show_nsfw = Some(
|
|
||||||
luv.local_user.show_nsfw
|
|
||||||
);
|
|
||||||
this.state.saveUserSettingsForm.theme = Some(
|
|
||||||
luv.local_user.theme ? luv.local_user.theme : "browser"
|
|
||||||
);
|
|
||||||
this.state.saveUserSettingsForm.default_sort_type = Some(
|
|
||||||
luv.local_user.default_sort_type
|
|
||||||
);
|
|
||||||
this.state.saveUserSettingsForm.default_listing_type = Some(
|
|
||||||
luv.local_user.default_listing_type
|
|
||||||
);
|
|
||||||
this.state.saveUserSettingsForm.lang = Some(luv.local_user.lang);
|
|
||||||
this.state.saveUserSettingsForm.avatar = luv.person.avatar;
|
|
||||||
this.state.saveUserSettingsForm.banner = luv.person.banner;
|
|
||||||
this.state.saveUserSettingsForm.display_name = luv.person.display_name;
|
|
||||||
this.state.saveUserSettingsForm.show_avatars = Some(
|
|
||||||
luv.local_user.show_avatars
|
|
||||||
);
|
|
||||||
this.state.saveUserSettingsForm.bot_account = Some(
|
|
||||||
luv.person.bot_account
|
|
||||||
);
|
|
||||||
this.state.saveUserSettingsForm.show_bot_accounts = Some(
|
|
||||||
luv.local_user.show_bot_accounts
|
|
||||||
);
|
|
||||||
this.state.saveUserSettingsForm.show_scores = Some(
|
|
||||||
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) {
|
||||||
let op = wsUserOp(msg);
|
let op = wsUserOp(msg);
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
|
@ -1196,15 +1214,13 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
} else if (op == UserOperation.SaveUserSettings) {
|
} else if (op == UserOperation.SaveUserSettings) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
this.state.saveUserSettingsLoading = false;
|
this.setState({ saveUserSettingsLoading: false });
|
||||||
this.setState(this.state);
|
|
||||||
toast(i18n.t("saved"));
|
toast(i18n.t("saved"));
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
} else if (op == UserOperation.ChangePassword) {
|
} else if (op == UserOperation.ChangePassword) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
this.state.changePasswordLoading = false;
|
this.setState({ changePasswordLoading: false });
|
||||||
this.setState(this.state);
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
toast(i18n.t("password_changed"));
|
toast(i18n.t("password_changed"));
|
||||||
} else if (op == UserOperation.DeleteAccount) {
|
} else if (op == UserOperation.DeleteAccount) {
|
||||||
|
|
|
@ -66,15 +66,15 @@ export class VerifyEmail extends Component<any, State> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="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}
|
description={None}
|
||||||
image={None}
|
image={None}
|
||||||
/>
|
/>
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
<div className="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||||
<h5>{i18n.t("verify_email")}</h5>
|
<h5>{i18n.t("verify_email")}</h5>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -94,8 +94,7 @@ export class VerifyEmail extends Component<any, State> {
|
||||||
let data = wsJsonToRes<VerifyEmailResponse>(msg, VerifyEmailResponse);
|
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.setState(this.emptyState);
|
||||||
this.setState(this.state);
|
|
||||||
this.props.history.push("/login");
|
this.props.history.push("/login");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,23 +50,27 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.handlePostCreate = this.handlePostCreate.bind(this);
|
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
|
|
||||||
|
this.handlePostCreate = this.handlePostCreate.bind(this);
|
||||||
|
|
||||||
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
if (UserService.Instance.myUserInfo.isNone() && 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`);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
|
||||||
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) {
|
||||||
this.state.listCommunitiesResponse = Some(
|
this.state = {
|
||||||
this.isoData.routeData[0] as ListCommunitiesResponse
|
...this.state,
|
||||||
);
|
listCommunitiesResponse: Some(
|
||||||
this.state.loading = false;
|
this.isoData.routeData[0] as ListCommunitiesResponse
|
||||||
|
),
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this.refetch();
|
this.refetch();
|
||||||
}
|
}
|
||||||
|
@ -123,7 +127,7 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
|
@ -137,8 +141,8 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
) : (
|
) : (
|
||||||
this.state.listCommunitiesResponse.match({
|
this.state.listCommunitiesResponse.match({
|
||||||
some: res => (
|
some: res => (
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
<div className="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||||
<h5>{i18n.t("create_post")}</h5>
|
<h5>{i18n.t("create_post")}</h5>
|
||||||
<PostForm
|
<PostForm
|
||||||
post_view={None}
|
post_view={None}
|
||||||
|
@ -147,6 +151,7 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
params={Some(this.params)}
|
params={Some(this.params)}
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -230,16 +235,15 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
msg,
|
msg,
|
||||||
ListCommunitiesResponse
|
ListCommunitiesResponse
|
||||||
);
|
);
|
||||||
this.state.listCommunitiesResponse = Some(data);
|
this.setState({ listCommunitiesResponse: Some(data), loading: false });
|
||||||
this.state.loading = false;
|
|
||||||
this.setState(this.state);
|
|
||||||
} else if (op == UserOperation.GetCommunity) {
|
} else if (op == UserOperation.GetCommunity) {
|
||||||
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
||||||
this.state.listCommunitiesResponse = Some({
|
this.setState({
|
||||||
communities: [data.community_view],
|
listCommunitiesResponse: Some({
|
||||||
|
communities: [data.community_view],
|
||||||
|
}),
|
||||||
|
loading: false,
|
||||||
});
|
});
|
||||||
this.state.loading = false;
|
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,27 +34,33 @@ export class MetadataCard extends Component<
|
||||||
some: embedTitle =>
|
some: embedTitle =>
|
||||||
post.url.match({
|
post.url.match({
|
||||||
some: url => (
|
some: url => (
|
||||||
<div class="card border-secondary mt-3 mb-2">
|
<div className="card border-secondary mt-3 mb-2">
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<div class="card-body">
|
<div className="card-body">
|
||||||
{post.name !== embedTitle && [
|
{post.name !== embedTitle && (
|
||||||
<h5 class="card-title d-inline">
|
<>
|
||||||
<a class="text-body" href={url} rel={relTags}>
|
<h5 className="card-title d-inline">
|
||||||
{embedTitle}
|
<a
|
||||||
</a>
|
className="text-body"
|
||||||
</h5>,
|
href={url}
|
||||||
<span class="d-inline-block ml-2 mb-2 small text-muted">
|
rel={relTags}
|
||||||
<a
|
>
|
||||||
class="text-muted font-italic"
|
{embedTitle}
|
||||||
href={url}
|
</a>
|
||||||
rel={relTags}
|
</h5>
|
||||||
>
|
<span className="d-inline-block ml-2 mb-2 small text-muted">
|
||||||
{new URL(url).hostname}
|
<a
|
||||||
<Icon icon="external-link" classes="ml-1" />
|
className="text-muted font-italic"
|
||||||
</a>
|
href={url}
|
||||||
</span>,
|
rel={relTags}
|
||||||
]}
|
>
|
||||||
|
{new URL(url).hostname}
|
||||||
|
<Icon icon="external-link" classes="ml-1" />
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
{post.embed_description.match({
|
{post.embed_description.match({
|
||||||
some: desc => (
|
some: desc => (
|
||||||
<div
|
<div
|
||||||
|
@ -66,9 +72,9 @@ export class MetadataCard extends Component<
|
||||||
),
|
),
|
||||||
none: <></>,
|
none: <></>,
|
||||||
})}
|
})}
|
||||||
{post.embed_html.isSome() && (
|
{post.embed_video_url.isSome() && (
|
||||||
<button
|
<button
|
||||||
class="mt-2 btn btn-secondary text-monospace"
|
className="mt-2 btn btn-secondary text-monospace"
|
||||||
onClick={linkEvent(this, this.handleIframeExpand)}
|
onClick={linkEvent(this, this.handleIframeExpand)}
|
||||||
data-tippy-content={i18n.t("expand_here")}
|
data-tippy-content={i18n.t("expand_here")}
|
||||||
>
|
>
|
||||||
|
@ -85,10 +91,10 @@ export class MetadataCard extends Component<
|
||||||
none: <></>,
|
none: <></>,
|
||||||
})}
|
})}
|
||||||
{this.state.expanded &&
|
{this.state.expanded &&
|
||||||
post.embed_html.match({
|
post.embed_video_url.match({
|
||||||
some: html => (
|
some: html => (
|
||||||
<div
|
<div
|
||||||
class="mt-3 mb-2"
|
className="mt-3 mb-2"
|
||||||
dangerouslySetInnerHTML={{ __html: html }}
|
dangerouslySetInnerHTML={{ __html: html }}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
@ -99,7 +105,6 @@ export class MetadataCard extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleIframeExpand(i: MetadataCard) {
|
handleIframeExpand(i: MetadataCard) {
|
||||||
i.state.expanded = !i.state.expanded;
|
i.setState({ expanded: !i.state.expanded });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
CommunityView,
|
CommunityView,
|
||||||
CreatePost,
|
CreatePost,
|
||||||
EditPost,
|
EditPost,
|
||||||
|
Language,
|
||||||
ListingType,
|
ListingType,
|
||||||
PostResponse,
|
PostResponse,
|
||||||
PostView,
|
PostView,
|
||||||
|
@ -36,6 +37,7 @@ import {
|
||||||
ghostArchiveUrl,
|
ghostArchiveUrl,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
isImage,
|
isImage,
|
||||||
|
myFirstDiscussionLanguageId,
|
||||||
pictrsDeleteToast,
|
pictrsDeleteToast,
|
||||||
relTags,
|
relTags,
|
||||||
setupTippy,
|
setupTippy,
|
||||||
|
@ -48,6 +50,7 @@ import {
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { Icon, Spinner } from "../common/icon";
|
import { Icon, Spinner } from "../common/icon";
|
||||||
|
import { LanguageSelect } from "../common/language-select";
|
||||||
import { MarkdownTextArea } from "../common/markdown-textarea";
|
import { MarkdownTextArea } from "../common/markdown-textarea";
|
||||||
import { PostListings } from "./post-listings";
|
import { PostListings } from "./post-listings";
|
||||||
|
|
||||||
|
@ -60,6 +63,7 @@ const MAX_POST_TITLE_LENGTH = 200;
|
||||||
|
|
||||||
interface PostFormProps {
|
interface PostFormProps {
|
||||||
post_view: Option<PostView>; // If a post is given, that means this is an edit
|
post_view: Option<PostView>; // If a post is given, that means this is an edit
|
||||||
|
allLanguages: Language[];
|
||||||
communities: Option<CommunityView[]>;
|
communities: Option<CommunityView[]>;
|
||||||
params: Option<PostFormParams>;
|
params: Option<PostFormParams>;
|
||||||
onCancel?(): any;
|
onCancel?(): any;
|
||||||
|
@ -76,6 +80,7 @@ interface PostFormState {
|
||||||
crossPosts: Option<PostView[]>;
|
crossPosts: Option<PostView[]>;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
imageLoading: boolean;
|
imageLoading: boolean;
|
||||||
|
communitySearchLoading: boolean;
|
||||||
previewMode: boolean;
|
previewMode: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,10 +95,12 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
url: None,
|
url: None,
|
||||||
body: None,
|
body: None,
|
||||||
honeypot: None,
|
honeypot: None,
|
||||||
|
language_id: None,
|
||||||
auth: undefined,
|
auth: undefined,
|
||||||
}),
|
}),
|
||||||
loading: false,
|
loading: false,
|
||||||
imageLoading: false,
|
imageLoading: false,
|
||||||
|
communitySearchLoading: false,
|
||||||
previewMode: false,
|
previewMode: false,
|
||||||
suggestedTitle: None,
|
suggestedTitle: None,
|
||||||
suggestedPosts: None,
|
suggestedPosts: None,
|
||||||
|
@ -105,35 +112,44 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
this.fetchSimilarPosts = debounce(this.fetchSimilarPosts.bind(this));
|
this.fetchSimilarPosts = debounce(this.fetchSimilarPosts.bind(this));
|
||||||
this.fetchPageTitle = debounce(this.fetchPageTitle.bind(this));
|
this.fetchPageTitle = debounce(this.fetchPageTitle.bind(this));
|
||||||
this.handlePostBodyChange = this.handlePostBodyChange.bind(this);
|
this.handlePostBodyChange = this.handlePostBodyChange.bind(this);
|
||||||
|
this.handleLanguageChange = this.handleLanguageChange.bind(this);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
|
|
||||||
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
// Means its an edit
|
// Means its an edit
|
||||||
this.props.post_view.match({
|
if (this.props.post_view.isSome()) {
|
||||||
some: pv =>
|
let pv = this.props.post_view.unwrap();
|
||||||
(this.state.postForm = new CreatePost({
|
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
postForm: new CreatePost({
|
||||||
body: pv.post.body,
|
body: pv.post.body,
|
||||||
name: pv.post.name,
|
name: pv.post.name,
|
||||||
community_id: pv.community.id,
|
community_id: pv.community.id,
|
||||||
url: pv.post.url,
|
url: pv.post.url,
|
||||||
nsfw: Some(pv.post.nsfw),
|
nsfw: Some(pv.post.nsfw),
|
||||||
honeypot: None,
|
honeypot: None,
|
||||||
|
language_id: Some(pv.post.language_id),
|
||||||
auth: auth().unwrap(),
|
auth: auth().unwrap(),
|
||||||
})),
|
}),
|
||||||
none: void 0,
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
this.props.params.match({
|
if (this.props.params.isSome()) {
|
||||||
some: params => {
|
let params = this.props.params.unwrap();
|
||||||
this.state.postForm.name = toUndefined(params.name);
|
this.state = {
|
||||||
this.state.postForm.url = params.url;
|
...this.state,
|
||||||
this.state.postForm.body = params.body;
|
postForm: {
|
||||||
},
|
...this.state.postForm,
|
||||||
none: void 0,
|
name: toUndefined(params.name),
|
||||||
});
|
url: params.url,
|
||||||
|
body: params.body,
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
},
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -165,6 +181,10 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let selectedLangs = this.state.postForm.language_id
|
||||||
|
.or(myFirstDiscussionLanguageId(UserService.Instance.myUserInfo))
|
||||||
|
.map(Array.of);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Prompt
|
<Prompt
|
||||||
|
@ -177,15 +197,15 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
message={i18n.t("block_leaving")}
|
message={i18n.t("block_leaving")}
|
||||||
/>
|
/>
|
||||||
<form onSubmit={linkEvent(this, this.handlePostSubmit)}>
|
<form onSubmit={linkEvent(this, this.handlePostSubmit)}>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="post-url">
|
<label className="col-sm-2 col-form-label" htmlFor="post-url">
|
||||||
{i18n.t("url")}
|
{i18n.t("url")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<input
|
<input
|
||||||
type="url"
|
type="url"
|
||||||
id="post-url"
|
id="post-url"
|
||||||
class="form-control"
|
className="form-control"
|
||||||
value={toUndefined(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)}
|
||||||
|
@ -193,7 +213,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
{this.state.suggestedTitle.match({
|
{this.state.suggestedTitle.match({
|
||||||
some: title => (
|
some: title => (
|
||||||
<div
|
<div
|
||||||
class="mt-1 text-muted small font-weight-bold pointer"
|
className="mt-1 text-muted small font-weight-bold pointer"
|
||||||
role="button"
|
role="button"
|
||||||
onClick={linkEvent(this, this.copySuggestedTitle)}
|
onClick={linkEvent(this, this.copySuggestedTitle)}
|
||||||
>
|
>
|
||||||
|
@ -217,7 +237,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
type="file"
|
type="file"
|
||||||
accept="image/*,video/*"
|
accept="image/*,video/*"
|
||||||
name="file"
|
name="file"
|
||||||
class="d-none"
|
className="d-none"
|
||||||
disabled={UserService.Instance.myUserInfo.isNone()}
|
disabled={UserService.Instance.myUserInfo.isNone()}
|
||||||
onChange={linkEvent(this, this.handleImageUpload)}
|
onChange={linkEvent(this, this.handleImageUpload)}
|
||||||
/>
|
/>
|
||||||
|
@ -230,7 +250,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
href={`${webArchiveUrl}/save/${encodeURIComponent(
|
href={`${webArchiveUrl}/save/${encodeURIComponent(
|
||||||
url
|
url
|
||||||
)}`}
|
)}`}
|
||||||
class="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
||||||
rel={relTags}
|
rel={relTags}
|
||||||
>
|
>
|
||||||
archive.org {i18n.t("archive_link")}
|
archive.org {i18n.t("archive_link")}
|
||||||
|
@ -239,7 +259,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
href={`${ghostArchiveUrl}/search?term=${encodeURIComponent(
|
href={`${ghostArchiveUrl}/search?term=${encodeURIComponent(
|
||||||
url
|
url
|
||||||
)}`}
|
)}`}
|
||||||
class="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
||||||
rel={relTags}
|
rel={relTags}
|
||||||
>
|
>
|
||||||
ghostarchive.org {i18n.t("archive_link")}
|
ghostarchive.org {i18n.t("archive_link")}
|
||||||
|
@ -248,7 +268,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
href={`${archiveTodayUrl}/?run=1&url=${encodeURIComponent(
|
href={`${archiveTodayUrl}/?run=1&url=${encodeURIComponent(
|
||||||
url
|
url
|
||||||
)}`}
|
)}`}
|
||||||
class="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
||||||
rel={relTags}
|
rel={relTags}
|
||||||
>
|
>
|
||||||
archive.today {i18n.t("archive_link")}
|
archive.today {i18n.t("archive_link")}
|
||||||
|
@ -260,14 +280,16 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
{this.state.imageLoading && <Spinner />}
|
{this.state.imageLoading && <Spinner />}
|
||||||
{this.state.postForm.url.match({
|
{this.state.postForm.url.match({
|
||||||
some: url =>
|
some: url =>
|
||||||
isImage(url) && <img src={url} class="img-fluid" alt="" />,
|
isImage(url) && (
|
||||||
|
<img src={url} className="img-fluid" alt="" />
|
||||||
|
),
|
||||||
none: <></>,
|
none: <></>,
|
||||||
})}
|
})}
|
||||||
{this.state.crossPosts.match({
|
{this.state.crossPosts.match({
|
||||||
some: xPosts =>
|
some: xPosts =>
|
||||||
xPosts.length > 0 && (
|
xPosts.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<div class="my-1 text-muted small font-weight-bold">
|
<div className="my-1 text-muted small font-weight-bold">
|
||||||
{i18n.t("cross_posts")}
|
{i18n.t("cross_posts")}
|
||||||
</div>
|
</div>
|
||||||
<PostListings
|
<PostListings
|
||||||
|
@ -275,6 +297,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
posts={xPosts}
|
posts={xPosts}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
@ -282,16 +305,16 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="post-title">
|
<label className="col-sm-2 col-form-label" htmlFor="post-title">
|
||||||
{i18n.t("title")}
|
{i18n.t("title")}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<textarea
|
<textarea
|
||||||
value={this.state.postForm.name}
|
value={this.state.postForm.name}
|
||||||
id="post-title"
|
id="post-title"
|
||||||
onInput={linkEvent(this, this.handlePostNameChange)}
|
onInput={linkEvent(this, this.handlePostNameChange)}
|
||||||
class={`form-control ${
|
className={`form-control ${
|
||||||
!validTitle(this.state.postForm.name) && "is-invalid"
|
!validTitle(this.state.postForm.name) && "is-invalid"
|
||||||
}`}
|
}`}
|
||||||
required
|
required
|
||||||
|
@ -300,7 +323,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
maxLength={MAX_POST_TITLE_LENGTH}
|
maxLength={MAX_POST_TITLE_LENGTH}
|
||||||
/>
|
/>
|
||||||
{!validTitle(this.state.postForm.name) && (
|
{!validTitle(this.state.postForm.name) && (
|
||||||
<div class="invalid-feedback">
|
<div className="invalid-feedback">
|
||||||
{i18n.t("invalid_post_title")}
|
{i18n.t("invalid_post_title")}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -308,7 +331,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
some: sPosts =>
|
some: sPosts =>
|
||||||
sPosts.length > 0 && (
|
sPosts.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<div class="my-1 text-muted small font-weight-bold">
|
<div className="my-1 text-muted small font-weight-bold">
|
||||||
{i18n.t("related_posts")}
|
{i18n.t("related_posts")}
|
||||||
</div>
|
</div>
|
||||||
<PostListings
|
<PostListings
|
||||||
|
@ -316,6 +339,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
posts={sPosts}
|
posts={sPosts}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
@ -324,33 +348,42 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label">{i18n.t("body")}</label>
|
<label className="col-sm-2 col-form-label">{i18n.t("body")}</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.postForm.body}
|
initialContent={this.state.postForm.body}
|
||||||
|
initialLanguageId={None}
|
||||||
onContentChange={this.handlePostBodyChange}
|
onContentChange={this.handlePostBodyChange}
|
||||||
placeholder={None}
|
placeholder={None}
|
||||||
buttonTitle={None}
|
buttonTitle={None}
|
||||||
maxLength={None}
|
maxLength={None}
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{this.props.post_view.isNone() && (
|
{this.props.post_view.isNone() && (
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="post-community">
|
<label
|
||||||
{i18n.t("community")}
|
className="col-sm-2 col-form-label"
|
||||||
|
htmlFor="post-community"
|
||||||
|
>
|
||||||
|
{this.state.communitySearchLoading ? (
|
||||||
|
<Spinner />
|
||||||
|
) : (
|
||||||
|
i18n.t("community")
|
||||||
|
)}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<select
|
<select
|
||||||
class="form-control"
|
className="form-control"
|
||||||
id="post-community"
|
id="post-community"
|
||||||
value={this.state.postForm.community_id}
|
value={this.state.postForm.community_id}
|
||||||
onInput={linkEvent(this, this.handlePostCommunityChange)}
|
onInput={linkEvent(this, this.handlePostCommunityChange)}
|
||||||
>
|
>
|
||||||
<option>{i18n.t("select_a_community")}</option>
|
<option>{i18n.t("select_a_community")}</option>
|
||||||
{this.props.communities.unwrapOr([]).map(cv => (
|
{this.props.communities.unwrapOr([]).map(cv => (
|
||||||
<option value={cv.community.id}>
|
<option key={cv.community.id} value={cv.community.id}>
|
||||||
{communitySelectName(cv)}
|
{communitySelectName(cv)}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
|
@ -359,14 +392,14 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{this.props.enableNsfw && (
|
{this.props.enableNsfw && (
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<legend class="col-form-label col-sm-2 pt-0">
|
<legend className="col-form-label col-sm-2 pt-0">
|
||||||
{i18n.t("nsfw")}
|
{i18n.t("nsfw")}
|
||||||
</legend>
|
</legend>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input position-static"
|
className="form-check-input position-static"
|
||||||
id="post-nsfw"
|
id="post-nsfw"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(this.state.postForm.nsfw)}
|
checked={toUndefined(this.state.postForm.nsfw)}
|
||||||
|
@ -376,24 +409,30 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<LanguageSelect
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
|
selectedLanguageIds={selectedLangs}
|
||||||
|
multiple={false}
|
||||||
|
onChange={this.handleLanguageChange}
|
||||||
|
/>
|
||||||
<input
|
<input
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
autoComplete="false"
|
autoComplete="false"
|
||||||
name="a_password"
|
name="a_password"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control honeypot"
|
className="form-control honeypot"
|
||||||
id="register-honey"
|
id="register-honey"
|
||||||
value={toUndefined(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 className="form-group row">
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<button
|
<button
|
||||||
disabled={
|
disabled={
|
||||||
!this.state.postForm.community_id || this.state.loading
|
!this.state.postForm.community_id || this.state.loading
|
||||||
}
|
}
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-secondary mr-2"
|
className="btn btn-secondary mr-2"
|
||||||
>
|
>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
|
@ -406,7 +445,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
{this.props.post_view.isSome() && (
|
{this.props.post_view.isSome() && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
onClick={linkEvent(this, this.handleCancel)}
|
onClick={linkEvent(this, this.handleCancel)}
|
||||||
>
|
>
|
||||||
{i18n.t("cancel")}
|
{i18n.t("cancel")}
|
||||||
|
@ -422,12 +461,14 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
handlePostSubmit(i: PostForm, event: any) {
|
handlePostSubmit(i: PostForm, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
i.setState({ loading: true });
|
||||||
|
|
||||||
// Coerce empty url string to undefined
|
// Coerce empty url string to undefined
|
||||||
if (
|
if (
|
||||||
i.state.postForm.url.isSome() &&
|
i.state.postForm.url.isSome() &&
|
||||||
i.state.postForm.url.unwrapOr("blank") === ""
|
i.state.postForm.url.unwrapOr("blank") === ""
|
||||||
) {
|
) {
|
||||||
i.state.postForm.url = None;
|
i.setState(s => ((s.postForm.url = None), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
let pForm = i.state.postForm;
|
let pForm = i.state.postForm;
|
||||||
|
@ -439,37 +480,39 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
body: pForm.body,
|
body: pForm.body,
|
||||||
nsfw: pForm.nsfw,
|
nsfw: pForm.nsfw,
|
||||||
post_id: pv.post.id,
|
post_id: pv.post.id,
|
||||||
|
language_id: Some(pv.post.language_id),
|
||||||
auth: auth().unwrap(),
|
auth: auth().unwrap(),
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.editPost(form));
|
WebSocketService.Instance.send(wsClient.editPost(form));
|
||||||
},
|
},
|
||||||
none: () => {
|
none: () => {
|
||||||
i.state.postForm.auth = auth().unwrap();
|
i.setState(s => ((s.postForm.auth = auth().unwrap()), s));
|
||||||
WebSocketService.Instance.send(wsClient.createPost(i.state.postForm));
|
let form = new CreatePost({ ...i.state.postForm });
|
||||||
|
WebSocketService.Instance.send(wsClient.createPost(form));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
i.state.loading = true;
|
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
copySuggestedTitle(i: PostForm) {
|
copySuggestedTitle(i: PostForm) {
|
||||||
i.state.suggestedTitle.match({
|
i.state.suggestedTitle.match({
|
||||||
some: sTitle => {
|
some: sTitle => {
|
||||||
i.state.postForm.name = sTitle.substring(0, MAX_POST_TITLE_LENGTH);
|
i.setState(
|
||||||
i.state.suggestedTitle = None;
|
s => (
|
||||||
|
(s.postForm.name = sTitle.substring(0, MAX_POST_TITLE_LENGTH)), s
|
||||||
|
)
|
||||||
|
);
|
||||||
|
i.setState({ suggestedTitle: None });
|
||||||
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);
|
|
||||||
},
|
},
|
||||||
none: void 0,
|
none: void 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostUrlChange(i: PostForm, event: any) {
|
handlePostUrlChange(i: PostForm, event: any) {
|
||||||
i.state.postForm.url = Some(event.target.value);
|
i.setState(s => ((s.postForm.url = Some(event.target.value)), s));
|
||||||
i.setState(i.state);
|
|
||||||
i.fetchPageTitle();
|
i.fetchPageTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,12 +537,10 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
|
|
||||||
// Fetch the page title
|
// Fetch the page title
|
||||||
getSiteMetadata(url).then(d => {
|
getSiteMetadata(url).then(d => {
|
||||||
this.state.suggestedTitle = d.metadata.title;
|
this.setState({ suggestedTitle: d.metadata.title });
|
||||||
this.setState(this.state);
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.state.suggestedTitle = None;
|
this.setState({ suggestedTitle: None, crossPosts: None });
|
||||||
this.state.crossPosts = None;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
none: void 0,
|
none: void 0,
|
||||||
|
@ -507,8 +548,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostNameChange(i: PostForm, event: any) {
|
handlePostNameChange(i: PostForm, event: any) {
|
||||||
i.state.postForm.name = event.target.value;
|
i.setState(s => ((s.postForm.name = event.target.value), s));
|
||||||
i.setState(i.state);
|
|
||||||
i.fetchSimilarPosts();
|
i.fetchSimilarPosts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,30 +569,30 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
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 = None;
|
this.setState({ suggestedPosts: None });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostBodyChange(val: string) {
|
handlePostBodyChange(val: string) {
|
||||||
this.state.postForm.body = Some(val);
|
this.setState(s => ((s.postForm.body = Some(val)), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostCommunityChange(i: PostForm, event: any) {
|
handlePostCommunityChange(i: PostForm, event: any) {
|
||||||
i.state.postForm.community_id = Number(event.target.value);
|
i.setState(
|
||||||
i.setState(i.state);
|
s => ((s.postForm.community_id = Number(event.target.value)), s)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostNsfwChange(i: PostForm, event: any) {
|
handlePostNsfwChange(i: PostForm, event: any) {
|
||||||
i.state.postForm.nsfw = Some(event.target.checked);
|
i.setState(s => ((s.postForm.nsfw = Some(event.target.checked)), s));
|
||||||
i.setState(i.state);
|
}
|
||||||
|
|
||||||
|
handleLanguageChange(val: number[]) {
|
||||||
|
this.setState(s => ((s.postForm.language_id = Some(val[0])), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleHoneyPotChange(i: PostForm, event: any) {
|
handleHoneyPotChange(i: PostForm, event: any) {
|
||||||
i.state.postForm.honeypot = Some(event.target.value);
|
i.setState(s => ((s.postForm.honeypot = Some(event.target.value)), s));
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancel(i: PostForm) {
|
handleCancel(i: PostForm) {
|
||||||
|
@ -561,8 +601,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
|
|
||||||
handlePreviewToggle(i: PostForm, event: any) {
|
handlePreviewToggle(i: PostForm, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.previewMode = !i.state.previewMode;
|
i.setState({ previewMode: !i.state.previewMode });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleImageUploadPaste(i: PostForm, event: any) {
|
handleImageUploadPaste(i: PostForm, event: any) {
|
||||||
|
@ -584,8 +623,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("images[]", file);
|
formData.append("images[]", file);
|
||||||
|
|
||||||
i.state.imageLoading = true;
|
i.setState({ imageLoading: true });
|
||||||
i.setState(i.state);
|
|
||||||
|
|
||||||
fetch(pictrsUri, {
|
fetch(pictrsUri, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -601,22 +639,19 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
let deleteToken = res.files[0].delete_token;
|
let deleteToken = res.files[0].delete_token;
|
||||||
let deleteUrl = `${pictrsUri}/delete/${deleteToken}/${hash}`;
|
let deleteUrl = `${pictrsUri}/delete/${deleteToken}/${hash}`;
|
||||||
i.state.postForm.url = Some(url);
|
i.state.postForm.url = Some(url);
|
||||||
i.state.imageLoading = false;
|
i.setState({ imageLoading: false });
|
||||||
i.setState(i.state);
|
|
||||||
pictrsDeleteToast(
|
pictrsDeleteToast(
|
||||||
i18n.t("click_to_delete_picture"),
|
i18n.t("click_to_delete_picture"),
|
||||||
i18n.t("picture_deleted"),
|
i18n.t("picture_deleted"),
|
||||||
deleteUrl
|
deleteUrl
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
i.state.imageLoading = false;
|
i.setState({ imageLoading: false });
|
||||||
i.setState(i.state);
|
|
||||||
toast(JSON.stringify(res), "danger");
|
toast(JSON.stringify(res), "danger");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
i.state.imageLoading = false;
|
i.setState({ imageLoading: false });
|
||||||
i.setState(i.state);
|
|
||||||
console.error(error);
|
console.error(error);
|
||||||
toast(error, "danger");
|
toast(error, "danger");
|
||||||
});
|
});
|
||||||
|
@ -631,11 +666,17 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
this.choices.passedElement.element.addEventListener(
|
this.choices.passedElement.element.addEventListener(
|
||||||
"choice",
|
"choice",
|
||||||
(e: any) => {
|
(e: any) => {
|
||||||
this.state.postForm.community_id = Number(e.detail.choice.value);
|
this.setState(
|
||||||
this.setState(this.state);
|
s => (
|
||||||
|
(s.postForm.community_id = Number(e.detail.choice.value)), s
|
||||||
|
)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
this.choices.passedElement.element.addEventListener("search", () => {
|
||||||
|
this.setState({ communitySearchLoading: true });
|
||||||
|
});
|
||||||
this.choices.passedElement.element.addEventListener(
|
this.choices.passedElement.element.addEventListener(
|
||||||
"search",
|
"search",
|
||||||
debounce(async (e: any) => {
|
debounce(async (e: any) => {
|
||||||
|
@ -648,6 +689,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
"label",
|
"label",
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
this.setState({ communitySearchLoading: false });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
|
@ -658,7 +700,8 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.post_view.match({
|
this.props.post_view.match({
|
||||||
some: pv => (this.state.postForm.community_id = pv.community.id),
|
some: pv =>
|
||||||
|
this.setState(s => ((s.postForm.community_id = pv.community.id), s)),
|
||||||
none: void 0,
|
none: void 0,
|
||||||
});
|
});
|
||||||
this.props.params.match({
|
this.props.params.match({
|
||||||
|
@ -670,9 +713,12 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
let foundCommunityId = this.props.communities
|
let foundCommunityId = this.props.communities
|
||||||
.unwrapOr([])
|
.unwrapOr([])
|
||||||
.find(r => r.community.name == name).community.id;
|
.find(r => r.community.name == name).community.id;
|
||||||
this.state.postForm.community_id = foundCommunityId;
|
this.setState(
|
||||||
|
s => ((s.postForm.community_id = foundCommunityId), s)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
right: id => (this.state.postForm.community_id = id),
|
right: id =>
|
||||||
|
this.setState(s => ((s.postForm.community_id = id), s)),
|
||||||
}),
|
}),
|
||||||
none: void 0,
|
none: void 0,
|
||||||
}),
|
}),
|
||||||
|
@ -693,15 +739,13 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
// Errors handled by top level pages
|
// Errors handled by top level pages
|
||||||
// toast(i18n.t(msg.error), "danger");
|
// toast(i18n.t(msg.error), "danger");
|
||||||
this.state.loading = false;
|
this.setState({ loading: false });
|
||||||
this.setState(this.state);
|
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.CreatePost) {
|
} else if (op == UserOperation.CreatePost) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
||||||
UserService.Instance.myUserInfo.match({
|
UserService.Instance.myUserInfo.match({
|
||||||
some: mui => {
|
some: mui => {
|
||||||
if (data.post_view.creator.id == mui.local_user_view.person.id) {
|
if (data.post_view.creator.id == mui.local_user_view.person.id) {
|
||||||
this.state.loading = false;
|
|
||||||
this.props.onCreate(data.post_view);
|
this.props.onCreate(data.post_view);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -712,7 +756,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
UserService.Instance.myUserInfo.match({
|
UserService.Instance.myUserInfo.match({
|
||||||
some: mui => {
|
some: mui => {
|
||||||
if (data.post_view.creator.id == mui.local_user_view.person.id) {
|
if (data.post_view.creator.id == mui.local_user_view.person.id) {
|
||||||
this.state.loading = false;
|
this.setState({ loading: false });
|
||||||
this.props.onEdit(data.post_view);
|
this.props.onEdit(data.post_view);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -722,11 +766,10 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
|
let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
|
||||||
|
|
||||||
if (data.type_ == SearchType[SearchType.Posts]) {
|
if (data.type_ == SearchType[SearchType.Posts]) {
|
||||||
this.state.suggestedPosts = Some(data.posts);
|
this.setState({ suggestedPosts: Some(data.posts) });
|
||||||
} else if (data.type_ == SearchType[SearchType.Url]) {
|
} else if (data.type_ == SearchType[SearchType.Url]) {
|
||||||
this.state.crossPosts = Some(data.posts);
|
this.setState({ crossPosts: Some(data.posts) });
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
CreatePostLike,
|
CreatePostLike,
|
||||||
CreatePostReport,
|
CreatePostReport,
|
||||||
DeletePost,
|
DeletePost,
|
||||||
|
Language,
|
||||||
LockPost,
|
LockPost,
|
||||||
PersonViewSafe,
|
PersonViewSafe,
|
||||||
PostView,
|
PostView,
|
||||||
|
@ -88,6 +89,7 @@ interface PostListingProps {
|
||||||
duplicates: Option<PostView[]>;
|
duplicates: Option<PostView[]>;
|
||||||
moderators: Option<CommunityModeratorView[]>;
|
moderators: Option<CommunityModeratorView[]>;
|
||||||
admins: Option<PersonViewSafe[]>;
|
admins: Option<PersonViewSafe[]>;
|
||||||
|
allLanguages: Language[];
|
||||||
showCommunity?: boolean;
|
showCommunity?: boolean;
|
||||||
showBody?: boolean;
|
showBody?: boolean;
|
||||||
enableDownvotes?: boolean;
|
enableDownvotes?: boolean;
|
||||||
|
@ -135,20 +137,21 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: PostListingProps) {
|
componentWillReceiveProps(nextProps: PostListingProps) {
|
||||||
this.state.my_vote = nextProps.post_view.my_vote;
|
this.setState({
|
||||||
this.state.upvotes = nextProps.post_view.counts.upvotes;
|
my_vote: nextProps.post_view.my_vote,
|
||||||
this.state.downvotes = nextProps.post_view.counts.downvotes;
|
upvotes: nextProps.post_view.counts.upvotes,
|
||||||
this.state.score = nextProps.post_view.counts.score;
|
downvotes: nextProps.post_view.counts.downvotes,
|
||||||
|
score: nextProps.post_view.counts.score,
|
||||||
|
});
|
||||||
if (this.props.post_view.post.id !== nextProps.post_view.post.id) {
|
if (this.props.post_view.post.id !== nextProps.post_view.post.id) {
|
||||||
this.state.imageExpanded = false;
|
this.setState({ imageExpanded: false });
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let post = this.props.post_view.post;
|
let post = this.props.post_view.post;
|
||||||
return (
|
return (
|
||||||
<div class="post-listing">
|
<div className="post-listing">
|
||||||
{!this.state.showEdit ? (
|
{!this.state.showEdit ? (
|
||||||
<>
|
<>
|
||||||
{this.listing()}
|
{this.listing()}
|
||||||
|
@ -159,7 +162,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
{this.showBody && this.body()}
|
{this.showBody && this.body()}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<PostForm
|
<PostForm
|
||||||
post_view={Some(this.props.post_view)}
|
post_view={Some(this.props.post_view)}
|
||||||
communities={None}
|
communities={None}
|
||||||
|
@ -168,6 +171,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
onCancel={this.handleEditCancel}
|
onCancel={this.handleEditCancel}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -178,7 +182,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
body() {
|
body() {
|
||||||
return this.props.post_view.post.body.match({
|
return this.props.post_view.post.body.match({
|
||||||
some: body => (
|
some: body => (
|
||||||
<div class="col-12 card my-2 p-2">
|
<div className="col-12 card my-2 p-2">
|
||||||
{this.state.viewSource ? (
|
{this.state.viewSource ? (
|
||||||
<pre>{body}</pre>
|
<pre>{body}</pre>
|
||||||
) : (
|
) : (
|
||||||
|
@ -194,14 +198,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
return this.imageSrc.match({
|
return this.imageSrc.match({
|
||||||
some: src => (
|
some: src => (
|
||||||
<>
|
<>
|
||||||
<div class="offset-sm-3 my-2 d-none d-sm-block">
|
<div className="offset-sm-3 my-2 d-none d-sm-block">
|
||||||
<a href={src} class="d-inline-block">
|
<a href={src} className="d-inline-block">
|
||||||
<PictrsImage src={src} />
|
<PictrsImage src={src} />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="my-2 d-block d-sm-none">
|
<div className="my-2 d-block d-sm-none">
|
||||||
<a
|
<a
|
||||||
class="d-inline-block"
|
className="d-inline-block"
|
||||||
onClick={linkEvent(this, this.handleImageExpandClick)}
|
onClick={linkEvent(this, this.handleImageExpandClick)}
|
||||||
>
|
>
|
||||||
<PictrsImage src={src} />
|
<PictrsImage src={src} />
|
||||||
|
@ -254,7 +258,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
href={this.imageSrc.unwrap()}
|
href={this.imageSrc.unwrap()}
|
||||||
class="text-body d-inline-block position-relative mb-2"
|
className="text-body d-inline-block position-relative mb-2"
|
||||||
data-tippy-content={i18n.t("expand_here")}
|
data-tippy-content={i18n.t("expand_here")}
|
||||||
onClick={linkEvent(this, this.handleImageExpandClick)}
|
onClick={linkEvent(this, this.handleImageExpandClick)}
|
||||||
aria-label={i18n.t("expand_here")}
|
aria-label={i18n.t("expand_here")}
|
||||||
|
@ -266,7 +270,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
} else if (url.isSome() && thumbnail.isSome()) {
|
} else if (url.isSome() && thumbnail.isSome()) {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
class="text-body d-inline-block position-relative mb-2"
|
className="text-body d-inline-block position-relative mb-2"
|
||||||
href={url.unwrap()}
|
href={url.unwrap()}
|
||||||
rel={relTags}
|
rel={relTags}
|
||||||
title={url.unwrap()}
|
title={url.unwrap()}
|
||||||
|
@ -278,13 +282,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
} else if (url.isSome()) {
|
} else if (url.isSome()) {
|
||||||
if (isVideo(url.unwrap())) {
|
if (isVideo(url.unwrap())) {
|
||||||
return (
|
return (
|
||||||
<div class="embed-responsive embed-responsive-16by9">
|
<div className="embed-responsive embed-responsive-16by9">
|
||||||
<video
|
<video
|
||||||
playsinline
|
playsInline
|
||||||
muted
|
muted
|
||||||
loop
|
loop
|
||||||
controls
|
controls
|
||||||
class="embed-responsive-item"
|
className="embed-responsive-item"
|
||||||
>
|
>
|
||||||
<source src={url.unwrap()} type="video/mp4" />
|
<source src={url.unwrap()} type="video/mp4" />
|
||||||
</video>
|
</video>
|
||||||
|
@ -298,7 +302,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
title={url.unwrap()}
|
title={url.unwrap()}
|
||||||
rel={relTags}
|
rel={relTags}
|
||||||
>
|
>
|
||||||
<div class="thumbnail rounded bg-light d-flex justify-content-center">
|
<div className="thumbnail rounded bg-light d-flex justify-content-center">
|
||||||
<Icon icon="external-link" classes="d-flex align-items-center" />
|
<Icon icon="external-link" classes="d-flex align-items-center" />
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
@ -311,7 +315,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
to={`/post/${post.id}`}
|
to={`/post/${post.id}`}
|
||||||
title={i18n.t("comments")}
|
title={i18n.t("comments")}
|
||||||
>
|
>
|
||||||
<div class="thumbnail rounded bg-light d-flex justify-content-center">
|
<div className="thumbnail rounded bg-light d-flex justify-content-center">
|
||||||
<Icon icon="message-square" classes="d-flex align-items-center" />
|
<Icon icon="message-square" classes="d-flex align-items-center" />
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -322,7 +326,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
createdLine() {
|
createdLine() {
|
||||||
let post_view = this.props.post_view;
|
let post_view = this.props.post_view;
|
||||||
return (
|
return (
|
||||||
<ul class="list-inline mb-1 text-muted small">
|
<ul className="list-inline mb-1 text-muted small">
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<PersonListing person={post_view.creator} />
|
<PersonListing person={post_view.creator} />
|
||||||
|
|
||||||
|
@ -346,7 +350,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
)}
|
)}
|
||||||
{this.props.showCommunity && (
|
{this.props.showCommunity && (
|
||||||
<span>
|
<span>
|
||||||
<span class="mx-1"> {i18n.t("to")} </span>
|
<span className="mx-1"> {i18n.t("to")} </span>
|
||||||
<CommunityLink community={post_view.community} />
|
<CommunityLink community={post_view.community} />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
@ -416,13 +420,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</button>
|
</button>
|
||||||
{showScores() ? (
|
{showScores() ? (
|
||||||
<div
|
<div
|
||||||
class={`unselectable pointer font-weight-bold text-muted px-1`}
|
className={`unselectable pointer font-weight-bold text-muted px-1`}
|
||||||
data-tippy-content={this.pointsTippy}
|
data-tippy-content={this.pointsTippy}
|
||||||
>
|
>
|
||||||
{numToSI(this.state.score)}
|
{numToSI(this.state.score)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div class="p-1"></div>
|
<div className="p-1"></div>
|
||||||
)}
|
)}
|
||||||
{this.props.enableDownvotes && (
|
{this.props.enableDownvotes && (
|
||||||
<button
|
<button
|
||||||
|
@ -470,7 +474,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
})}
|
})}
|
||||||
{post.url.map(isImage).or(post.thumbnail_url).unwrapOr(false) && (
|
{post.url.map(isImage).or(post.thumbnail_url).unwrapOr(false) && (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link text-monospace text-muted small d-inline-block ml-2"
|
className="btn btn-link text-monospace text-muted small d-inline-block ml-2"
|
||||||
data-tippy-content={i18n.t("expand_here")}
|
data-tippy-content={i18n.t("expand_here")}
|
||||||
onClick={linkEvent(this, this.handleImageExpandClick)}
|
onClick={linkEvent(this, this.handleImageExpandClick)}
|
||||||
>
|
>
|
||||||
|
@ -525,13 +529,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
return this.props.duplicates.match({
|
return this.props.duplicates.match({
|
||||||
some: dupes =>
|
some: dupes =>
|
||||||
dupes.length > 0 && (
|
dupes.length > 0 && (
|
||||||
<ul class="list-inline mb-1 small text-muted">
|
<ul className="list-inline mb-1 small text-muted">
|
||||||
<>
|
<>
|
||||||
<li className="list-inline-item mr-2">
|
<li className="list-inline-item mr-2">
|
||||||
{i18n.t("cross_posted_to")}
|
{i18n.t("cross_posted_to")}
|
||||||
</li>
|
</li>
|
||||||
{dupes.map(pv => (
|
{dupes.map(pv => (
|
||||||
<li className="list-inline-item mr-2">
|
<li key={pv.post.id} className="list-inline-item mr-2">
|
||||||
<Link to={`/post/${pv.post.id}`}>
|
<Link to={`/post/${pv.post.id}`}>
|
||||||
{pv.community.local
|
{pv.community.local
|
||||||
? pv.community.name
|
? pv.community.name
|
||||||
|
@ -551,7 +555,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
commentsLine(mobile = false) {
|
commentsLine(mobile = false) {
|
||||||
let post = this.props.post_view.post;
|
let post = this.props.post_view.post;
|
||||||
return (
|
return (
|
||||||
<div class="d-flex justify-content-start flex-wrap text-muted font-weight-bold mb-1">
|
<div className="d-flex justify-content-start flex-wrap text-muted font-weight-bold mb-1">
|
||||||
{this.commentsButton}
|
{this.commentsButton}
|
||||||
{!post.local && (
|
{!post.local && (
|
||||||
<a
|
<a
|
||||||
|
@ -617,7 +621,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
get commentsButton() {
|
get commentsButton() {
|
||||||
let post_view = this.props.post_view;
|
let post_view = this.props.post_view;
|
||||||
return (
|
return (
|
||||||
<button class="btn btn-link text-muted py-0 pl-0">
|
<button className="btn btn-link text-muted py-0 pl-0">
|
||||||
<Link
|
<Link
|
||||||
className="text-muted"
|
className="text-muted"
|
||||||
title={i18n.t("number_of_comments", {
|
title={i18n.t("number_of_comments", {
|
||||||
|
@ -627,15 +631,34 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
to={`/post/${post_view.post.id}?scrollToComments=true`}
|
to={`/post/${post_view.post.id}?scrollToComments=true`}
|
||||||
>
|
>
|
||||||
<Icon icon="message-square" classes="mr-1" inline />
|
<Icon icon="message-square" classes="mr-1" inline />
|
||||||
{i18n.t("number_of_comments", {
|
<span className="mr-2">
|
||||||
count: post_view.counts.comments,
|
{i18n.t("number_of_comments", {
|
||||||
formattedCount: numToSI(post_view.counts.comments),
|
count: post_view.counts.comments,
|
||||||
|
formattedCount: numToSI(post_view.counts.comments),
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
{this.unreadCount.match({
|
||||||
|
some: unreadCount => (
|
||||||
|
<span className="small text-warning">
|
||||||
|
({unreadCount} {i18n.t("new")})
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
})}
|
})}
|
||||||
</Link>
|
</Link>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get unreadCount(): Option<number> {
|
||||||
|
let pv = this.props.post_view;
|
||||||
|
if (pv.unread_comments == pv.counts.comments || pv.unread_comments == 0) {
|
||||||
|
return None;
|
||||||
|
} else {
|
||||||
|
return Some(pv.unread_comments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get mobileVotes() {
|
get mobileVotes() {
|
||||||
// TODO: make nicer
|
// TODO: make nicer
|
||||||
let tippy = showScores() ? { "data-tippy-content": this.pointsTippy } : {};
|
let tippy = showScores() ? { "data-tippy-content": this.pointsTippy } : {};
|
||||||
|
@ -652,7 +675,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
>
|
>
|
||||||
<Icon icon="arrow-up1" classes="icon-inline small" />
|
<Icon icon="arrow-up1" classes="icon-inline small" />
|
||||||
{showScores() && (
|
{showScores() && (
|
||||||
<span class="ml-2">{numToSI(this.state.upvotes)}</span>
|
<span className="ml-2">{numToSI(this.state.upvotes)}</span>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
{this.props.enableDownvotes && (
|
{this.props.enableDownvotes && (
|
||||||
|
@ -669,7 +692,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
<Icon icon="arrow-down1" classes="icon-inline small" />
|
<Icon icon="arrow-down1" classes="icon-inline small" />
|
||||||
{showScores() && (
|
{showScores() && (
|
||||||
<span
|
<span
|
||||||
class={classNames("ml-2", {
|
className={classNames("ml-2", {
|
||||||
invisible: this.state.downvotes === 0,
|
invisible: this.state.downvotes === 0,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
@ -688,7 +711,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
let label = saved ? i18n.t("unsave") : i18n.t("save");
|
let label = saved ? i18n.t("unsave") : i18n.t("save");
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleSavePostClick)}
|
onClick={linkEvent(this, this.handleSavePostClick)}
|
||||||
data-tippy-content={label}
|
data-tippy-content={label}
|
||||||
aria-label={label}
|
aria-label={label}
|
||||||
|
@ -717,7 +740,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
get reportButton() {
|
get reportButton() {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleShowReportDialog)}
|
onClick={linkEvent(this, this.handleShowReportDialog)}
|
||||||
data-tippy-content={i18n.t("show_report_dialog")}
|
data-tippy-content={i18n.t("show_report_dialog")}
|
||||||
aria-label={i18n.t("show_report_dialog")}
|
aria-label={i18n.t("show_report_dialog")}
|
||||||
|
@ -730,7 +753,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
get blockButton() {
|
get blockButton() {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleBlockUserClick)}
|
onClick={linkEvent(this, this.handleBlockUserClick)}
|
||||||
data-tippy-content={i18n.t("block_user")}
|
data-tippy-content={i18n.t("block_user")}
|
||||||
aria-label={i18n.t("block_user")}
|
aria-label={i18n.t("block_user")}
|
||||||
|
@ -743,7 +766,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
get editButton() {
|
get editButton() {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleEditClick)}
|
onClick={linkEvent(this, this.handleEditClick)}
|
||||||
data-tippy-content={i18n.t("edit")}
|
data-tippy-content={i18n.t("edit")}
|
||||||
aria-label={i18n.t("edit")}
|
aria-label={i18n.t("edit")}
|
||||||
|
@ -758,7 +781,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
let label = !deleted ? i18n.t("delete") : i18n.t("restore");
|
let label = !deleted ? i18n.t("delete") : i18n.t("restore");
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleDeleteClick)}
|
onClick={linkEvent(this, this.handleDeleteClick)}
|
||||||
data-tippy-content={label}
|
data-tippy-content={label}
|
||||||
aria-label={label}
|
aria-label={label}
|
||||||
|
@ -775,7 +798,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
get showMoreButton() {
|
get showMoreButton() {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleShowAdvanced)}
|
onClick={linkEvent(this, this.handleShowAdvanced)}
|
||||||
data-tippy-content={i18n.t("more")}
|
data-tippy-content={i18n.t("more")}
|
||||||
aria-label={i18n.t("more")}
|
aria-label={i18n.t("more")}
|
||||||
|
@ -788,7 +811,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
get viewSourceButton() {
|
get viewSourceButton() {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleViewSource)}
|
onClick={linkEvent(this, this.handleViewSource)}
|
||||||
data-tippy-content={i18n.t("view_source")}
|
data-tippy-content={i18n.t("view_source")}
|
||||||
aria-label={i18n.t("view_source")}
|
aria-label={i18n.t("view_source")}
|
||||||
|
@ -807,7 +830,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
let label = locked ? i18n.t("unlock") : i18n.t("lock");
|
let label = locked ? i18n.t("unlock") : i18n.t("lock");
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleModLock)}
|
onClick={linkEvent(this, this.handleModLock)}
|
||||||
data-tippy-content={label}
|
data-tippy-content={label}
|
||||||
aria-label={label}
|
aria-label={label}
|
||||||
|
@ -826,7 +849,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
let label = stickied ? i18n.t("unsticky") : i18n.t("sticky");
|
let label = stickied ? i18n.t("unsticky") : i18n.t("sticky");
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleModSticky)}
|
onClick={linkEvent(this, this.handleModSticky)}
|
||||||
data-tippy-content={label}
|
data-tippy-content={label}
|
||||||
aria-label={label}
|
aria-label={label}
|
||||||
|
@ -844,7 +867,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
let removed = this.props.post_view.post.removed;
|
let removed = this.props.post_view.post.removed;
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
!removed ? this.handleModRemoveShow : this.handleModRemoveSubmit
|
!removed ? this.handleModRemoveShow : this.handleModRemoveSubmit
|
||||||
|
@ -870,7 +893,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
{!this.creatorIsMod_ &&
|
{!this.creatorIsMod_ &&
|
||||||
(!post_view.creator_banned_from_community ? (
|
(!post_view.creator_banned_from_community ? (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleModBanFromCommunityShow
|
this.handleModBanFromCommunityShow
|
||||||
|
@ -881,7 +904,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleModBanFromCommunitySubmit
|
this.handleModBanFromCommunitySubmit
|
||||||
|
@ -893,7 +916,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
))}
|
))}
|
||||||
{!post_view.creator_banned_from_community && (
|
{!post_view.creator_banned_from_community && (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleAddModToCommunity)}
|
onClick={linkEvent(this, this.handleAddModToCommunity)}
|
||||||
aria-label={
|
aria-label={
|
||||||
this.creatorIsMod_
|
this.creatorIsMod_
|
||||||
|
@ -914,7 +937,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
this.creatorIsMod_ &&
|
this.creatorIsMod_ &&
|
||||||
(!this.state.showConfirmTransferCommunity ? (
|
(!this.state.showConfirmTransferCommunity ? (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleShowConfirmTransferCommunity
|
this.handleShowConfirmTransferCommunity
|
||||||
|
@ -926,20 +949,20 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
class="d-inline-block mr-1 btn btn-link btn-animate text-muted py-0"
|
className="d-inline-block mr-1 btn btn-link btn-animate text-muted py-0"
|
||||||
aria-label={i18n.t("are_you_sure")}
|
aria-label={i18n.t("are_you_sure")}
|
||||||
>
|
>
|
||||||
{i18n.t("are_you_sure")}
|
{i18n.t("are_you_sure")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0 d-inline-block mr-1"
|
className="btn btn-link btn-animate text-muted py-0 d-inline-block mr-1"
|
||||||
aria-label={i18n.t("yes")}
|
aria-label={i18n.t("yes")}
|
||||||
onClick={linkEvent(this, this.handleTransferCommunity)}
|
onClick={linkEvent(this, this.handleTransferCommunity)}
|
||||||
>
|
>
|
||||||
{i18n.t("yes")}
|
{i18n.t("yes")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0 d-inline-block"
|
className="btn btn-link btn-animate text-muted py-0 d-inline-block"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleCancelShowConfirmTransferCommunity
|
this.handleCancelShowConfirmTransferCommunity
|
||||||
|
@ -957,7 +980,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
<>
|
<>
|
||||||
{!isBanned(post_view.creator) ? (
|
{!isBanned(post_view.creator) ? (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleModBanShow)}
|
onClick={linkEvent(this, this.handleModBanShow)}
|
||||||
aria-label={i18n.t("ban_from_site")}
|
aria-label={i18n.t("ban_from_site")}
|
||||||
>
|
>
|
||||||
|
@ -965,7 +988,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleModBanSubmit)}
|
onClick={linkEvent(this, this.handleModBanSubmit)}
|
||||||
aria-label={i18n.t("unban_from_site")}
|
aria-label={i18n.t("unban_from_site")}
|
||||||
>
|
>
|
||||||
|
@ -973,14 +996,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handlePurgePersonShow)}
|
onClick={linkEvent(this, this.handlePurgePersonShow)}
|
||||||
aria-label={i18n.t("purge_user")}
|
aria-label={i18n.t("purge_user")}
|
||||||
>
|
>
|
||||||
{i18n.t("purge_user")}
|
{i18n.t("purge_user")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handlePurgePostShow)}
|
onClick={linkEvent(this, this.handlePurgePostShow)}
|
||||||
aria-label={i18n.t("purge_post")}
|
aria-label={i18n.t("purge_post")}
|
||||||
>
|
>
|
||||||
|
@ -990,7 +1013,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
)}
|
)}
|
||||||
{!isBanned(post_view.creator) && post_view.creator.local && (
|
{!isBanned(post_view.creator) && post_view.creator.local && (
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleAddAdmin)}
|
onClick={linkEvent(this, this.handleAddAdmin)}
|
||||||
aria-label={
|
aria-label={
|
||||||
this.creatorIsAdmin_
|
this.creatorIsAdmin_
|
||||||
|
@ -1022,23 +1045,23 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
<>
|
<>
|
||||||
{this.state.showRemoveDialog && (
|
{this.state.showRemoveDialog && (
|
||||||
<form
|
<form
|
||||||
class="form-inline"
|
className="form-inline"
|
||||||
onSubmit={linkEvent(this, this.handleModRemoveSubmit)}
|
onSubmit={linkEvent(this, this.handleModRemoveSubmit)}
|
||||||
>
|
>
|
||||||
<label class="sr-only" htmlFor="post-listing-remove-reason">
|
<label className="sr-only" htmlFor="post-listing-remove-reason">
|
||||||
{i18n.t("reason")}
|
{i18n.t("reason")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="post-listing-remove-reason"
|
id="post-listing-remove-reason"
|
||||||
class="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("reason")}
|
placeholder={i18n.t("reason")}
|
||||||
value={toUndefined(this.state.removeReason)}
|
value={toUndefined(this.state.removeReason)}
|
||||||
onInput={linkEvent(this, this.handleModRemoveReasonChange)}
|
onInput={linkEvent(this, this.handleModRemoveReasonChange)}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
aria-label={i18n.t("remove_post")}
|
aria-label={i18n.t("remove_post")}
|
||||||
>
|
>
|
||||||
{i18n.t("remove_post")}
|
{i18n.t("remove_post")}
|
||||||
|
@ -1047,40 +1070,43 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
)}
|
)}
|
||||||
{this.state.showBanDialog && (
|
{this.state.showBanDialog && (
|
||||||
<form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}>
|
||||||
<div class="form-group row col-12">
|
<div className="form-group row col-12">
|
||||||
<label class="col-form-label" htmlFor="post-listing-ban-reason">
|
<label
|
||||||
|
className="col-form-label"
|
||||||
|
htmlFor="post-listing-ban-reason"
|
||||||
|
>
|
||||||
{i18n.t("reason")}
|
{i18n.t("reason")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="post-listing-ban-reason"
|
id="post-listing-ban-reason"
|
||||||
class="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("reason")}
|
placeholder={i18n.t("reason")}
|
||||||
value={toUndefined(this.state.banReason)}
|
value={toUndefined(this.state.banReason)}
|
||||||
onInput={linkEvent(this, this.handleModBanReasonChange)}
|
onInput={linkEvent(this, this.handleModBanReasonChange)}
|
||||||
/>
|
/>
|
||||||
<label class="col-form-label" htmlFor={`mod-ban-expires`}>
|
<label className="col-form-label" htmlFor={`mod-ban-expires`}>
|
||||||
{i18n.t("expires")}
|
{i18n.t("expires")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
id={`mod-ban-expires`}
|
id={`mod-ban-expires`}
|
||||||
class="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("number_of_days")}
|
placeholder={i18n.t("number_of_days")}
|
||||||
value={toUndefined(this.state.banExpireDays)}
|
value={toUndefined(this.state.banExpireDays)}
|
||||||
onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
|
onInput={linkEvent(this, this.handleModBanExpireDaysChange)}
|
||||||
/>
|
/>
|
||||||
<div class="form-group">
|
<div className="form-group">
|
||||||
<div class="form-check">
|
<div className="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
className="form-check-input"
|
||||||
id="mod-ban-remove-data"
|
id="mod-ban-remove-data"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.removeData}
|
checked={this.state.removeData}
|
||||||
onChange={linkEvent(this, this.handleModRemoveDataChange)}
|
onChange={linkEvent(this, this.handleModRemoveDataChange)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
class="form-check-label"
|
className="form-check-label"
|
||||||
htmlFor="mod-ban-remove-data"
|
htmlFor="mod-ban-remove-data"
|
||||||
title={i18n.t("remove_content_more")}
|
title={i18n.t("remove_content_more")}
|
||||||
>
|
>
|
||||||
|
@ -1094,10 +1120,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
{/* <label class="col-form-label">Expires</label> */}
|
{/* <label class="col-form-label">Expires</label> */}
|
||||||
{/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
|
{/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
|
||||||
{/* </div> */}
|
{/* </div> */}
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
aria-label={i18n.t("ban")}
|
aria-label={i18n.t("ban")}
|
||||||
>
|
>
|
||||||
{i18n.t("ban")} {post.creator.name}
|
{i18n.t("ban")} {post.creator.name}
|
||||||
|
@ -1107,16 +1133,16 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
)}
|
)}
|
||||||
{this.state.showReportDialog && (
|
{this.state.showReportDialog && (
|
||||||
<form
|
<form
|
||||||
class="form-inline"
|
className="form-inline"
|
||||||
onSubmit={linkEvent(this, this.handleReportSubmit)}
|
onSubmit={linkEvent(this, this.handleReportSubmit)}
|
||||||
>
|
>
|
||||||
<label class="sr-only" htmlFor="post-report-reason">
|
<label className="sr-only" htmlFor="post-report-reason">
|
||||||
{i18n.t("reason")}
|
{i18n.t("reason")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="post-report-reason"
|
id="post-report-reason"
|
||||||
class="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("reason")}
|
placeholder={i18n.t("reason")}
|
||||||
required
|
required
|
||||||
value={toUndefined(this.state.reportReason)}
|
value={toUndefined(this.state.reportReason)}
|
||||||
|
@ -1124,7 +1150,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
aria-label={i18n.t("create_report")}
|
aria-label={i18n.t("create_report")}
|
||||||
>
|
>
|
||||||
{i18n.t("create_report")}
|
{i18n.t("create_report")}
|
||||||
|
@ -1133,17 +1159,17 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
)}
|
)}
|
||||||
{this.state.showPurgeDialog && (
|
{this.state.showPurgeDialog && (
|
||||||
<form
|
<form
|
||||||
class="form-inline"
|
className="form-inline"
|
||||||
onSubmit={linkEvent(this, this.handlePurgeSubmit)}
|
onSubmit={linkEvent(this, this.handlePurgeSubmit)}
|
||||||
>
|
>
|
||||||
<PurgeWarning />
|
<PurgeWarning />
|
||||||
<label class="sr-only" htmlFor="purge-reason">
|
<label className="sr-only" htmlFor="purge-reason">
|
||||||
{i18n.t("reason")}
|
{i18n.t("reason")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="purge-reason"
|
id="purge-reason"
|
||||||
class="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("reason")}
|
placeholder={i18n.t("reason")}
|
||||||
value={toUndefined(this.state.purgeReason)}
|
value={toUndefined(this.state.purgeReason)}
|
||||||
onInput={linkEvent(this, this.handlePurgeReasonChange)}
|
onInput={linkEvent(this, this.handlePurgeReasonChange)}
|
||||||
|
@ -1153,7 +1179,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
aria-label={purgeTypeText}
|
aria-label={purgeTypeText}
|
||||||
>
|
>
|
||||||
{purgeTypeText}
|
{purgeTypeText}
|
||||||
|
@ -1169,11 +1195,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
let post = this.props.post_view.post;
|
let post = this.props.post_view.post;
|
||||||
return post.thumbnail_url.isSome() ||
|
return post.thumbnail_url.isSome() ||
|
||||||
post.url.map(isImage).unwrapOr(false) ? (
|
post.url.map(isImage).unwrapOr(false) ? (
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div className={`${this.state.imageExpanded ? "col-12" : "col-8"}`}>
|
<div className={`${this.state.imageExpanded ? "col-12" : "col-8"}`}>
|
||||||
{this.postTitleLine()}
|
{this.postTitleLine()}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div className="col-4">
|
||||||
{/* Post body prev or thumbnail */}
|
{/* Post body prev or thumbnail */}
|
||||||
{!this.state.imageExpanded && this.thumbnail()}
|
{!this.state.imageExpanded && this.thumbnail()}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1198,9 +1224,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* The mobile view*/}
|
{/* The mobile view*/}
|
||||||
<div class="d-block d-sm-none">
|
<div className="d-block d-sm-none">
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
{this.createdLine()}
|
{this.createdLine()}
|
||||||
|
|
||||||
{/* If it has a thumbnail, do a right aligned thumbnail */}
|
{/* If it has a thumbnail, do a right aligned thumbnail */}
|
||||||
|
@ -1218,14 +1244,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* The larger view*/}
|
{/* The larger view*/}
|
||||||
<div class="d-none d-sm-block">
|
<div className="d-none d-sm-block">
|
||||||
<div class="row">
|
<div className="row">
|
||||||
{!this.props.viewOnly && this.voteBar()}
|
{!this.props.viewOnly && this.voteBar()}
|
||||||
<div class="col-sm-2 pr-0">
|
<div className="col-sm-2 pr-0">
|
||||||
<div class="">{this.thumbnail()}</div>
|
<div className="">{this.thumbnail()}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-sm-9">
|
<div className="col-12 col-sm-9">
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
{this.postTitleLine()}
|
{this.postTitleLine()}
|
||||||
{this.createdLine()}
|
{this.createdLine()}
|
||||||
|
@ -1260,18 +1286,24 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
let newVote = myVote == 1 ? 0 : 1;
|
let newVote = myVote == 1 ? 0 : 1;
|
||||||
|
|
||||||
if (myVote == 1) {
|
if (myVote == 1) {
|
||||||
this.state.score--;
|
this.setState({
|
||||||
this.state.upvotes--;
|
score: this.state.score - 1,
|
||||||
|
upvotes: this.state.upvotes - 1,
|
||||||
|
});
|
||||||
} else if (myVote == -1) {
|
} else if (myVote == -1) {
|
||||||
this.state.downvotes--;
|
this.setState({
|
||||||
this.state.upvotes++;
|
score: this.state.score + 2,
|
||||||
this.state.score += 2;
|
upvotes: this.state.upvotes + 1,
|
||||||
|
downvotes: this.state.downvotes - 1,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.state.upvotes++;
|
this.setState({
|
||||||
this.state.score++;
|
score: this.state.score + 1,
|
||||||
|
upvotes: this.state.upvotes + 1,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state.my_vote = Some(newVote);
|
this.setState({ my_vote: Some(newVote) });
|
||||||
|
|
||||||
let form = new CreatePostLike({
|
let form = new CreatePostLike({
|
||||||
post_id: this.props.post_view.post.id,
|
post_id: this.props.post_view.post.id,
|
||||||
|
@ -1294,18 +1326,24 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
let newVote = myVote == -1 ? 0 : -1;
|
let newVote = myVote == -1 ? 0 : -1;
|
||||||
|
|
||||||
if (myVote == 1) {
|
if (myVote == 1) {
|
||||||
this.state.score -= 2;
|
this.setState({
|
||||||
this.state.upvotes--;
|
score: this.state.score - 2,
|
||||||
this.state.downvotes++;
|
upvotes: this.state.upvotes - 1,
|
||||||
|
downvotes: this.state.downvotes + 1,
|
||||||
|
});
|
||||||
} else if (myVote == -1) {
|
} else if (myVote == -1) {
|
||||||
this.state.downvotes--;
|
this.setState({
|
||||||
this.state.score++;
|
score: this.state.score + 1,
|
||||||
|
downvotes: this.state.downvotes - 1,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.state.downvotes++;
|
this.setState({
|
||||||
this.state.score--;
|
score: this.state.score - 1,
|
||||||
|
downvotes: this.state.downvotes + 1,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state.my_vote = Some(newVote);
|
this.setState({ my_vote: Some(newVote) });
|
||||||
|
|
||||||
let form = new CreatePostLike({
|
let form = new CreatePostLike({
|
||||||
post_id: this.props.post_view.post.id,
|
post_id: this.props.post_view.post.id,
|
||||||
|
@ -1319,29 +1357,24 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditClick(i: PostListing) {
|
handleEditClick(i: PostListing) {
|
||||||
i.state.showEdit = true;
|
i.setState({ showEdit: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditCancel() {
|
handleEditCancel() {
|
||||||
this.state.showEdit = false;
|
this.setState({ showEdit: false });
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The actual editing is done in the recieve for post
|
// The actual editing is done in the recieve for post
|
||||||
handleEditPost() {
|
handleEditPost() {
|
||||||
this.state.showEdit = false;
|
this.setState({ showEdit: false });
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowReportDialog(i: PostListing) {
|
handleShowReportDialog(i: PostListing) {
|
||||||
i.state.showReportDialog = !i.state.showReportDialog;
|
i.setState({ showReportDialog: !i.state.showReportDialog });
|
||||||
i.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReportReasonChange(i: PostListing, event: any) {
|
handleReportReasonChange(i: PostListing, event: any) {
|
||||||
i.state.reportReason = Some(event.target.value);
|
i.setState({ reportReason: Some(event.target.value) });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReportSubmit(i: PostListing, event: any) {
|
handleReportSubmit(i: PostListing, event: any) {
|
||||||
|
@ -1353,8 +1386,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.createPostReport(form));
|
WebSocketService.Instance.send(wsClient.createPostReport(form));
|
||||||
|
|
||||||
i.state.showReportDialog = false;
|
i.setState({ showReportDialog: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBlockUserClick(i: PostListing) {
|
handleBlockUserClick(i: PostListing) {
|
||||||
|
@ -1413,19 +1445,18 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveShow(i: PostListing) {
|
handleModRemoveShow(i: PostListing) {
|
||||||
i.state.showRemoveDialog = !i.state.showRemoveDialog;
|
i.setState({
|
||||||
i.state.showBanDialog = false;
|
showRemoveDialog: !i.state.showRemoveDialog,
|
||||||
i.setState(i.state);
|
showBanDialog: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveReasonChange(i: PostListing, event: any) {
|
handleModRemoveReasonChange(i: PostListing, event: any) {
|
||||||
i.state.removeReason = Some(event.target.value);
|
i.setState({ removeReason: Some(event.target.value) });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveDataChange(i: PostListing, event: any) {
|
handleModRemoveDataChange(i: PostListing, event: any) {
|
||||||
i.state.removeData = event.target.checked;
|
i.setState({ removeData: event.target.checked });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveSubmit(i: PostListing, event: any) {
|
handleModRemoveSubmit(i: PostListing, event: any) {
|
||||||
|
@ -1438,8 +1469,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.removePost(form));
|
WebSocketService.Instance.send(wsClient.removePost(form));
|
||||||
|
|
||||||
i.state.showRemoveDialog = false;
|
i.setState({ showRemoveDialog: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModLock(i: PostListing) {
|
handleModLock(i: PostListing) {
|
||||||
|
@ -1461,36 +1491,39 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanFromCommunityShow(i: PostListing) {
|
handleModBanFromCommunityShow(i: PostListing) {
|
||||||
i.state.showBanDialog = true;
|
i.setState({
|
||||||
i.state.banType = BanType.Community;
|
showBanDialog: true,
|
||||||
i.state.showRemoveDialog = false;
|
banType: BanType.Community,
|
||||||
i.setState(i.state);
|
showRemoveDialog: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanShow(i: PostListing) {
|
handleModBanShow(i: PostListing) {
|
||||||
i.state.showBanDialog = true;
|
i.setState({
|
||||||
i.state.banType = BanType.Site;
|
showBanDialog: true,
|
||||||
i.state.showRemoveDialog = false;
|
banType: BanType.Site,
|
||||||
i.setState(i.state);
|
showRemoveDialog: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePurgePersonShow(i: PostListing) {
|
handlePurgePersonShow(i: PostListing) {
|
||||||
i.state.showPurgeDialog = true;
|
i.setState({
|
||||||
i.state.purgeType = PurgeType.Person;
|
showPurgeDialog: true,
|
||||||
i.state.showRemoveDialog = false;
|
purgeType: PurgeType.Person,
|
||||||
i.setState(i.state);
|
showRemoveDialog: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePurgePostShow(i: PostListing) {
|
handlePurgePostShow(i: PostListing) {
|
||||||
i.state.showPurgeDialog = true;
|
i.setState({
|
||||||
i.state.purgeType = PurgeType.Post;
|
showPurgeDialog: true,
|
||||||
i.state.showRemoveDialog = false;
|
purgeType: PurgeType.Post,
|
||||||
i.setState(i.state);
|
showRemoveDialog: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePurgeReasonChange(i: PostListing, event: any) {
|
handlePurgeReasonChange(i: PostListing, event: any) {
|
||||||
i.state.purgeReason = Some(event.target.value);
|
i.setState({ purgeReason: Some(event.target.value) });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePurgeSubmit(i: PostListing, event: any) {
|
handlePurgeSubmit(i: PostListing, event: any) {
|
||||||
|
@ -1512,29 +1545,24 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
WebSocketService.Instance.send(wsClient.purgePost(form));
|
WebSocketService.Instance.send(wsClient.purgePost(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
i.state.purgeLoading = true;
|
i.setState({ purgeLoading: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanReasonChange(i: PostListing, event: any) {
|
handleModBanReasonChange(i: PostListing, event: any) {
|
||||||
i.state.banReason = Some(event.target.value);
|
i.setState({ banReason: Some(event.target.value) });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanExpireDaysChange(i: PostListing, event: any) {
|
handleModBanExpireDaysChange(i: PostListing, event: any) {
|
||||||
i.state.banExpireDays = Some(event.target.value);
|
i.setState({ banExpireDays: Some(event.target.value) });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanFromCommunitySubmit(i: PostListing) {
|
handleModBanFromCommunitySubmit(i: PostListing) {
|
||||||
i.state.banType = BanType.Community;
|
i.setState({ banType: BanType.Community });
|
||||||
i.setState(i.state);
|
|
||||||
i.handleModBanBothSubmit(i);
|
i.handleModBanBothSubmit(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModBanSubmit(i: PostListing) {
|
handleModBanSubmit(i: PostListing) {
|
||||||
i.state.banType = BanType.Site;
|
i.setState({ banType: BanType.Site });
|
||||||
i.setState(i.state);
|
|
||||||
i.handleModBanBothSubmit(i);
|
i.handleModBanBothSubmit(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1545,7 +1573,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
// If its an unban, restore all their data
|
// If its an unban, restore all their data
|
||||||
let ban = !i.props.post_view.creator_banned_from_community;
|
let ban = !i.props.post_view.creator_banned_from_community;
|
||||||
if (ban == false) {
|
if (ban == false) {
|
||||||
i.state.removeData = false;
|
i.setState({ removeData: false });
|
||||||
}
|
}
|
||||||
let form = new BanFromCommunity({
|
let form = new BanFromCommunity({
|
||||||
person_id: i.props.post_view.creator.id,
|
person_id: i.props.post_view.creator.id,
|
||||||
|
@ -1561,7 +1589,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
// If its an unban, restore all their data
|
// If its an unban, restore all their data
|
||||||
let ban = !i.props.post_view.creator.banned;
|
let ban = !i.props.post_view.creator.banned;
|
||||||
if (ban == false) {
|
if (ban == false) {
|
||||||
i.state.removeData = false;
|
i.setState({ removeData: false });
|
||||||
}
|
}
|
||||||
let form = new BanPerson({
|
let form = new BanPerson({
|
||||||
person_id: i.props.post_view.creator.id,
|
person_id: i.props.post_view.creator.id,
|
||||||
|
@ -1574,8 +1602,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
WebSocketService.Instance.send(wsClient.banPerson(form));
|
WebSocketService.Instance.send(wsClient.banPerson(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
i.state.showBanDialog = false;
|
i.setState({ showBanDialog: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAddModToCommunity(i: PostListing) {
|
handleAddModToCommunity(i: PostListing) {
|
||||||
|
@ -1600,13 +1627,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowConfirmTransferCommunity(i: PostListing) {
|
handleShowConfirmTransferCommunity(i: PostListing) {
|
||||||
i.state.showConfirmTransferCommunity = true;
|
i.setState({ showConfirmTransferCommunity: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancelShowConfirmTransferCommunity(i: PostListing) {
|
handleCancelShowConfirmTransferCommunity(i: PostListing) {
|
||||||
i.state.showConfirmTransferCommunity = false;
|
i.setState({ showConfirmTransferCommunity: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTransferCommunity(i: PostListing) {
|
handleTransferCommunity(i: PostListing) {
|
||||||
|
@ -1616,48 +1641,42 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
auth: auth().unwrap(),
|
auth: auth().unwrap(),
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.transferCommunity(form));
|
WebSocketService.Instance.send(wsClient.transferCommunity(form));
|
||||||
i.state.showConfirmTransferCommunity = false;
|
i.setState({ showConfirmTransferCommunity: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowConfirmTransferSite(i: PostListing) {
|
handleShowConfirmTransferSite(i: PostListing) {
|
||||||
i.state.showConfirmTransferSite = true;
|
i.setState({ showConfirmTransferSite: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancelShowConfirmTransferSite(i: PostListing) {
|
handleCancelShowConfirmTransferSite(i: PostListing) {
|
||||||
i.state.showConfirmTransferSite = false;
|
i.setState({ showConfirmTransferSite: false });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleImageExpandClick(i: PostListing, event: any) {
|
handleImageExpandClick(i: PostListing, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.imageExpanded = !i.state.imageExpanded;
|
i.setState({ imageExpanded: !i.state.imageExpanded });
|
||||||
i.setState(i.state);
|
|
||||||
setupTippy();
|
setupTippy();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleViewSource(i: PostListing) {
|
handleViewSource(i: PostListing) {
|
||||||
i.state.viewSource = !i.state.viewSource;
|
i.setState({ viewSource: !i.state.viewSource });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowAdvanced(i: PostListing) {
|
handleShowAdvanced(i: PostListing) {
|
||||||
i.state.showAdvanced = !i.state.showAdvanced;
|
i.setState({ showAdvanced: !i.state.showAdvanced });
|
||||||
i.setState(i.state);
|
|
||||||
setupTippy();
|
setupTippy();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowMoreMobile(i: PostListing) {
|
handleShowMoreMobile(i: PostListing) {
|
||||||
i.state.showMoreMobile = !i.state.showMoreMobile;
|
i.setState({
|
||||||
i.state.showAdvanced = !i.state.showAdvanced;
|
showMoreMobile: !i.state.showMoreMobile,
|
||||||
i.setState(i.state);
|
showAdvanced: !i.state.showAdvanced,
|
||||||
|
});
|
||||||
setupTippy();
|
setupTippy();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowBody(i: PostListing) {
|
handleShowBody(i: PostListing) {
|
||||||
i.state.showBody = !i.state.showBody;
|
i.setState({ showBody: !i.state.showBody });
|
||||||
i.setState(i.state);
|
|
||||||
setupTippy();
|
setupTippy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,13 @@ 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";
|
||||||
import { PostView } from "lemmy-js-client";
|
import { Language, PostView } from "lemmy-js-client";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { PostListing } from "./post-listing";
|
import { PostListing } from "./post-listing";
|
||||||
|
|
||||||
interface PostListingsProps {
|
interface PostListingsProps {
|
||||||
posts: PostView[];
|
posts: PostView[];
|
||||||
|
allLanguages: Language[];
|
||||||
showCommunity?: boolean;
|
showCommunity?: boolean;
|
||||||
removeDuplicates?: boolean;
|
removeDuplicates?: boolean;
|
||||||
enableDownvotes: boolean;
|
enableDownvotes: boolean;
|
||||||
|
@ -41,8 +42,9 @@ export class PostListings extends Component<PostListingsProps, any> {
|
||||||
showCommunity={this.props.showCommunity}
|
showCommunity={this.props.showCommunity}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
|
allLanguages={this.props.allLanguages}
|
||||||
/>
|
/>
|
||||||
<hr class="my-3" />
|
<hr className="my-3" />
|
||||||
</>
|
</>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -45,6 +45,7 @@ export class PostReport extends Component<PostReportProps, any> {
|
||||||
read: false,
|
read: false,
|
||||||
creator_blocked: false,
|
creator_blocked: false,
|
||||||
my_vote: r.my_vote,
|
my_vote: r.my_vote,
|
||||||
|
unread_comments: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -58,6 +59,7 @@ export class PostReport extends Component<PostReportProps, any> {
|
||||||
enableDownvotes={true}
|
enableDownvotes={true}
|
||||||
enableNsfw={true}
|
enableNsfw={true}
|
||||||
viewOnly={true}
|
viewOnly={true}
|
||||||
|
allLanguages={[]}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
{i18n.t("reporter")}: <PersonListing person={r.creator} />
|
{i18n.t("reporter")}: <PersonListing person={r.creator} />
|
||||||
|
|
|
@ -120,28 +120,31 @@ export class Post extends Component<any, PostState> {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
this.state.commentSectionRef = createRef();
|
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
|
this.state = { ...this.state, commentSectionRef: createRef() };
|
||||||
|
|
||||||
// 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 = Some(this.isoData.routeData[0] as GetPostResponse);
|
this.state = {
|
||||||
this.state.commentsRes = Some(
|
...this.state,
|
||||||
this.isoData.routeData[1] as GetCommentsResponse
|
postRes: Some(this.isoData.routeData[0] as GetPostResponse),
|
||||||
);
|
commentsRes: Some(this.isoData.routeData[1] as GetCommentsResponse),
|
||||||
|
};
|
||||||
|
|
||||||
this.state.commentsRes.match({
|
if (this.state.commentsRes.isSome()) {
|
||||||
some: res => {
|
this.state = {
|
||||||
this.state.commentTree = buildCommentsTree(
|
...this.state,
|
||||||
res.comments,
|
commentTree: buildCommentsTree(
|
||||||
|
this.state.commentsRes.unwrap().comments,
|
||||||
this.state.commentId.isSome()
|
this.state.commentId.isSome()
|
||||||
);
|
),
|
||||||
},
|
};
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
this.state.loading = false;
|
this.state = { ...this.state, loading: false };
|
||||||
|
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
|
@ -305,8 +308,9 @@ export class Post extends Component<any, PostState> {
|
||||||
trackCommentsBoxScrolling = () => {
|
trackCommentsBoxScrolling = () => {
|
||||||
const wrappedElement = document.getElementsByClassName("comments")[0];
|
const wrappedElement = document.getElementsByClassName("comments")[0];
|
||||||
if (wrappedElement && this.isBottom(wrappedElement)) {
|
if (wrappedElement && this.isBottom(wrappedElement)) {
|
||||||
this.state.maxCommentsShown += commentsShownInterval;
|
this.setState({
|
||||||
this.setState(this.state);
|
maxCommentsShown: this.state.maxCommentsShown + commentsShownInterval,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -341,7 +345,7 @@ export class Post extends Component<any, PostState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<h5>
|
<h5>
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
|
@ -349,8 +353,8 @@ export class Post extends Component<any, PostState> {
|
||||||
) : (
|
) : (
|
||||||
this.state.postRes.match({
|
this.state.postRes.match({
|
||||||
some: res => (
|
some: res => (
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12 col-md-8 mb-3">
|
<div className="col-12 col-md-8 mb-3">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
|
@ -366,15 +370,17 @@ export class Post extends Component<any, PostState> {
|
||||||
admins={Some(this.state.siteRes.admins)}
|
admins={Some(this.state.siteRes.admins)}
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
<div ref={this.state.commentSectionRef} className="mb-2" />
|
<div ref={this.state.commentSectionRef} className="mb-2" />
|
||||||
<CommentForm
|
<CommentForm
|
||||||
node={Right(res.post_view.post.id)}
|
node={Right(res.post_view.post.id)}
|
||||||
disabled={res.post_view.post.locked}
|
disabled={res.post_view.post.locked}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
<div class="d-block d-md-none">
|
<div className="d-block d-md-none">
|
||||||
<button
|
<button
|
||||||
class="btn btn-secondary d-inline-block mb-2 mr-3"
|
className="btn btn-secondary d-inline-block mb-2 mr-3"
|
||||||
onClick={linkEvent(this, this.handleShowSidebarMobile)}
|
onClick={linkEvent(this, this.handleShowSidebarMobile)}
|
||||||
>
|
>
|
||||||
{i18n.t("sidebar")}{" "}
|
{i18n.t("sidebar")}{" "}
|
||||||
|
@ -395,7 +401,9 @@ export class Post extends Component<any, PostState> {
|
||||||
{this.state.commentViewType == CommentViewType.Flat &&
|
{this.state.commentViewType == CommentViewType.Flat &&
|
||||||
this.commentsFlat()}
|
this.commentsFlat()}
|
||||||
</div>
|
</div>
|
||||||
<div class="d-none d-md-block col-md-4">{this.sidebar()}</div>
|
<div className="d-none d-md-block col-md-4">
|
||||||
|
{this.sidebar()}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
none: <></>,
|
none: <></>,
|
||||||
|
@ -408,7 +416,7 @@ export class Post extends Component<any, PostState> {
|
||||||
sortRadios() {
|
sortRadios() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div class="btn-group btn-group-toggle flex-wrap mr-3 mb-2">
|
<div className="btn-group btn-group-toggle flex-wrap mr-3 mb-2">
|
||||||
<label
|
<label
|
||||||
className={`btn btn-outline-secondary pointer ${
|
className={`btn btn-outline-secondary pointer ${
|
||||||
CommentSortType[this.state.commentSort] === CommentSortType.Hot &&
|
CommentSortType[this.state.commentSort] === CommentSortType.Hot &&
|
||||||
|
@ -466,7 +474,7 @@ export class Post extends Component<any, PostState> {
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group btn-group-toggle flex-wrap mb-2">
|
<div className="btn-group btn-group-toggle flex-wrap mb-2">
|
||||||
<label
|
<label
|
||||||
className={`btn btn-outline-secondary pointer ${
|
className={`btn btn-outline-secondary pointer ${
|
||||||
this.state.commentViewType === CommentViewType.Flat && "active"
|
this.state.commentViewType === CommentViewType.Flat && "active"
|
||||||
|
@ -502,6 +510,7 @@ export class Post extends Component<any, PostState> {
|
||||||
admins={Some(this.state.siteRes.admins)}
|
admins={Some(this.state.siteRes.admins)}
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
showContext
|
showContext
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
@ -514,7 +523,7 @@ export class Post extends Component<any, PostState> {
|
||||||
sidebar() {
|
sidebar() {
|
||||||
return this.state.postRes.match({
|
return this.state.postRes.match({
|
||||||
some: res => (
|
some: res => (
|
||||||
<div class="mb-3">
|
<div className="mb-3">
|
||||||
<Sidebar
|
<Sidebar
|
||||||
community_view={res.community_view}
|
community_view={res.community_view}
|
||||||
moderators={res.moderators}
|
moderators={res.moderators}
|
||||||
|
@ -530,25 +539,26 @@ export class Post extends Component<any, PostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommentSortChange(i: Post, event: any) {
|
handleCommentSortChange(i: Post, event: any) {
|
||||||
i.state.commentSort = CommentSortType[event.target.value];
|
i.setState({
|
||||||
i.state.commentViewType = CommentViewType.Tree;
|
commentSort: CommentSortType[event.target.value],
|
||||||
i.setState(i.state);
|
commentViewType: CommentViewType.Tree,
|
||||||
|
});
|
||||||
i.fetchPost();
|
i.fetchPost();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommentViewTypeChange(i: Post, event: any) {
|
handleCommentViewTypeChange(i: Post, event: any) {
|
||||||
i.state.commentViewType = Number(event.target.value);
|
i.setState({
|
||||||
i.state.commentSort = CommentSortType.New;
|
commentViewType: Number(event.target.value),
|
||||||
i.state.commentTree = buildCommentsTree(
|
commentSort: CommentSortType.New,
|
||||||
i.state.commentsRes.map(r => r.comments).unwrapOr([]),
|
commentTree: buildCommentsTree(
|
||||||
i.state.commentId.isSome()
|
i.state.commentsRes.map(r => r.comments).unwrapOr([]),
|
||||||
);
|
i.state.commentId.isSome()
|
||||||
i.setState(i.state);
|
),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowSidebarMobile(i: Post) {
|
handleShowSidebarMobile(i: Post) {
|
||||||
i.state.showSidebarMobile = !i.state.showSidebarMobile;
|
i.setState({ showSidebarMobile: !i.state.showSidebarMobile });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleViewPost(i: Post) {
|
handleViewPost(i: Post) {
|
||||||
|
@ -581,14 +591,14 @@ export class Post extends Component<any, PostState> {
|
||||||
{this.state.commentId.isSome() && (
|
{this.state.commentId.isSome() && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
class="pl-0 d-block btn btn-link text-muted"
|
className="pl-0 d-block btn btn-link text-muted"
|
||||||
onClick={linkEvent(this, this.handleViewPost)}
|
onClick={linkEvent(this, this.handleViewPost)}
|
||||||
>
|
>
|
||||||
{i18n.t("view_all_comments")} ➔
|
{i18n.t("view_all_comments")} ➔
|
||||||
</button>
|
</button>
|
||||||
{showContextButton && (
|
{showContextButton && (
|
||||||
<button
|
<button
|
||||||
class="pl-0 d-block btn btn-link text-muted"
|
className="pl-0 d-block btn btn-link text-muted"
|
||||||
onClick={linkEvent(this, this.handleViewContext)}
|
onClick={linkEvent(this, this.handleViewContext)}
|
||||||
>
|
>
|
||||||
{i18n.t("show_context")} ➔
|
{i18n.t("show_context")} ➔
|
||||||
|
@ -604,6 +614,7 @@ export class Post extends Component<any, PostState> {
|
||||||
moderators={Some(res.moderators)}
|
moderators={Some(res.moderators)}
|
||||||
admins={Some(this.state.siteRes.admins)}
|
admins={Some(this.state.siteRes.admins)}
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
@ -636,7 +647,7 @@ export class Post extends Component<any, PostState> {
|
||||||
});
|
});
|
||||||
} else if (op == UserOperation.GetPost) {
|
} else if (op == UserOperation.GetPost) {
|
||||||
let data = wsJsonToRes<GetPostResponse>(msg, GetPostResponse);
|
let data = wsJsonToRes<GetPostResponse>(msg, GetPostResponse);
|
||||||
this.state.postRes = Some(data);
|
this.setState({ postRes: Some(data) });
|
||||||
|
|
||||||
// join the rooms
|
// join the rooms
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
|
@ -651,7 +662,6 @@ export class Post extends Component<any, PostState> {
|
||||||
// Get cross-posts
|
// Get cross-posts
|
||||||
// TODO move this into initial fetch and refetch
|
// TODO move this into initial fetch and refetch
|
||||||
this.fetchCrossPosts();
|
this.fetchCrossPosts();
|
||||||
this.setState(this.state);
|
|
||||||
setupTippy();
|
setupTippy();
|
||||||
if (this.state.commentId.isNone()) restoreScrollPosition(this.context);
|
if (this.state.commentId.isNone()) restoreScrollPosition(this.context);
|
||||||
|
|
||||||
|
@ -668,16 +678,16 @@ export class Post extends Component<any, PostState> {
|
||||||
newComments.shift();
|
newComments.shift();
|
||||||
res.comments.push(...newComments);
|
res.comments.push(...newComments);
|
||||||
},
|
},
|
||||||
none: () => {
|
none: () => this.setState({ commentsRes: Some(data) }),
|
||||||
this.state.commentsRes = Some(data);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
// this.state.commentsRes = Some(data);
|
// this.state.commentsRes = Some(data);
|
||||||
this.state.commentTree = buildCommentsTree(
|
this.setState({
|
||||||
this.state.commentsRes.map(r => r.comments).unwrapOr([]),
|
commentTree: buildCommentsTree(
|
||||||
this.state.commentId.isSome()
|
this.state.commentsRes.map(r => r.comments).unwrapOr([]),
|
||||||
);
|
this.state.commentId.isSome()
|
||||||
this.state.loading = false;
|
),
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.CreateComment) {
|
} else if (op == UserOperation.CreateComment) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
|
@ -827,19 +837,16 @@ export class Post extends Component<any, PostState> {
|
||||||
});
|
});
|
||||||
} else if (op == UserOperation.AddAdmin) {
|
} else if (op == UserOperation.AddAdmin) {
|
||||||
let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
|
let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
|
||||||
this.state.siteRes.admins = data.admins;
|
this.setState(s => ((s.siteRes.admins = data.admins), s));
|
||||||
this.setState(this.state);
|
|
||||||
} else if (op == UserOperation.Search) {
|
} else if (op == UserOperation.Search) {
|
||||||
let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
|
let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
|
||||||
let xPosts = data.posts.filter(
|
let xPosts = data.posts.filter(
|
||||||
p => p.post.id != Number(this.props.match.params.id)
|
p => p.post.id != Number(this.props.match.params.id)
|
||||||
);
|
);
|
||||||
this.state.crossPosts = xPosts.length > 0 ? Some(xPosts) : None;
|
this.setState({ crossPosts: xPosts.length > 0 ? Some(xPosts) : None });
|
||||||
this.setState(this.state);
|
|
||||||
} else if (op == UserOperation.LeaveAdmin) {
|
} else if (op == UserOperation.LeaveAdmin) {
|
||||||
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
||||||
this.state.siteRes = data;
|
this.setState({ siteRes: data });
|
||||||
this.setState(this.state);
|
|
||||||
} else if (op == UserOperation.TransferCommunity) {
|
} else if (op == UserOperation.TransferCommunity) {
|
||||||
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
||||||
this.state.postRes.match({
|
this.state.postRes.match({
|
||||||
|
|
|
@ -45,6 +45,7 @@ export class CreatePrivateMessage extends Component<
|
||||||
recipient_id: getRecipientIdFromProps(this.props),
|
recipient_id: getRecipientIdFromProps(this.props),
|
||||||
loading: true,
|
loading: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
|
@ -61,10 +62,13 @@ export class CreatePrivateMessage 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.recipientDetailsRes = Some(
|
this.state = {
|
||||||
this.isoData.routeData[0] as GetPersonDetailsResponse
|
...this.state,
|
||||||
);
|
recipientDetailsRes: Some(
|
||||||
this.state.loading = false;
|
this.isoData.routeData[0] as GetPersonDetailsResponse
|
||||||
|
),
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this.fetchPersonDetails();
|
this.fetchPersonDetails();
|
||||||
}
|
}
|
||||||
|
@ -115,7 +119,7 @@ export class CreatePrivateMessage extends Component<
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
|
@ -129,8 +133,8 @@ export class CreatePrivateMessage extends Component<
|
||||||
) : (
|
) : (
|
||||||
this.state.recipientDetailsRes.match({
|
this.state.recipientDetailsRes.match({
|
||||||
some: res => (
|
some: res => (
|
||||||
<div class="row">
|
<div className="row">
|
||||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
<div className="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||||
<h5>{i18n.t("create_private_message")}</h5>
|
<h5>{i18n.t("create_private_message")}</h5>
|
||||||
<PrivateMessageForm
|
<PrivateMessageForm
|
||||||
privateMessageView={None}
|
privateMessageView={None}
|
||||||
|
@ -151,7 +155,7 @@ export class CreatePrivateMessage extends Component<
|
||||||
toast(i18n.t("message_sent"));
|
toast(i18n.t("message_sent"));
|
||||||
|
|
||||||
// Navigate to the front
|
// Navigate to the front
|
||||||
this.context.router.history.push(`/`);
|
this.context.router.history.push("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -159,17 +163,14 @@ export class CreatePrivateMessage extends Component<
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
this.state.loading = false;
|
this.setState({ loading: false });
|
||||||
this.setState(this.state);
|
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.GetPersonDetails) {
|
} else if (op == UserOperation.GetPersonDetails) {
|
||||||
let data = wsJsonToRes<GetPersonDetailsResponse>(
|
let data = wsJsonToRes<GetPersonDetailsResponse>(
|
||||||
msg,
|
msg,
|
||||||
GetPersonDetailsResponse
|
GetPersonDetailsResponse
|
||||||
);
|
);
|
||||||
this.state.recipientDetailsRes = Some(data);
|
this.setState({ recipientDetailsRes: Some(data), loading: false });
|
||||||
this.state.loading = false;
|
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,11 +71,10 @@ export class PrivateMessageForm extends Component<
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
// Its an edit
|
// Its an edit
|
||||||
this.props.privateMessageView.match({
|
if (this.props.privateMessageView.isSome()) {
|
||||||
some: pm =>
|
this.state.privateMessageForm.content =
|
||||||
(this.state.privateMessageForm.content = pm.private_message.content),
|
this.props.privateMessageView.unwrap().private_message.content;
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -106,21 +105,21 @@ export class PrivateMessageForm extends Component<
|
||||||
/>
|
/>
|
||||||
<form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
|
<form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
|
||||||
{this.props.privateMessageView.isNone() && (
|
{this.props.privateMessageView.isNone() && (
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label">
|
<label className="col-sm-2 col-form-label">
|
||||||
{capitalizeFirstLetter(i18n.t("to"))}
|
{capitalizeFirstLetter(i18n.t("to"))}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="col-sm-10 form-control-plaintext">
|
<div className="col-sm-10 form-control-plaintext">
|
||||||
<PersonListing person={this.props.recipient} />
|
<PersonListing person={this.props.recipient} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<label class="col-sm-2 col-form-label">
|
<label className="col-sm-2 col-form-label">
|
||||||
{i18n.t("message")}
|
{i18n.t("message")}
|
||||||
<button
|
<button
|
||||||
class="btn btn-link text-warning d-inline-block"
|
className="btn btn-link text-warning d-inline-block"
|
||||||
onClick={linkEvent(this, this.handleShowDisclaimer)}
|
onClick={linkEvent(this, this.handleShowDisclaimer)}
|
||||||
data-tippy-content={i18n.t("private_message_disclaimer")}
|
data-tippy-content={i18n.t("private_message_disclaimer")}
|
||||||
aria-label={i18n.t("private_message_disclaimer")}
|
aria-label={i18n.t("private_message_disclaimer")}
|
||||||
|
@ -128,25 +127,27 @@ export class PrivateMessageForm extends Component<
|
||||||
<Icon icon="alert-triangle" classes="icon-inline" />
|
<Icon icon="alert-triangle" classes="icon-inline" />
|
||||||
</button>
|
</button>
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={Some(this.state.privateMessageForm.content)}
|
initialContent={Some(this.state.privateMessageForm.content)}
|
||||||
|
initialLanguageId={None}
|
||||||
placeholder={None}
|
placeholder={None}
|
||||||
buttonTitle={None}
|
buttonTitle={None}
|
||||||
maxLength={None}
|
maxLength={None}
|
||||||
onContentChange={this.handleContentChange}
|
onContentChange={this.handleContentChange}
|
||||||
|
allLanguages={[]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{this.state.showDisclaimer && (
|
{this.state.showDisclaimer && (
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="offset-sm-2 col-sm-10">
|
<div className="offset-sm-2 col-sm-10">
|
||||||
<div class="alert alert-danger" role="alert">
|
<div className="alert alert-danger" role="alert">
|
||||||
<T i18nKey="private_message_disclaimer">
|
<T i18nKey="private_message_disclaimer">
|
||||||
#
|
#
|
||||||
<a
|
<a
|
||||||
class="alert-link"
|
className="alert-link"
|
||||||
rel={relTags}
|
rel={relTags}
|
||||||
href="https://element.io/get-started"
|
href="https://element.io/get-started"
|
||||||
>
|
>
|
||||||
|
@ -157,11 +158,11 @@ export class PrivateMessageForm extends Component<
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div class="form-group row">
|
<div className="form-group row">
|
||||||
<div class="offset-sm-2 col-sm-10">
|
<div className="offset-sm-2 col-sm-10">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-secondary mr-2"
|
className="btn btn-secondary mr-2"
|
||||||
disabled={this.state.loading}
|
disabled={this.state.loading}
|
||||||
>
|
>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
|
@ -175,14 +176,14 @@ export class PrivateMessageForm extends Component<
|
||||||
{this.props.privateMessageView.isSome() && (
|
{this.props.privateMessageView.isSome() && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
onClick={linkEvent(this, this.handleCancel)}
|
onClick={linkEvent(this, this.handleCancel)}
|
||||||
>
|
>
|
||||||
{i18n.t("cancel")}
|
{i18n.t("cancel")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<ul class="d-inline-block float-right list-inline mb-1 text-muted font-weight-bold">
|
<ul className="d-inline-block float-right list-inline mb-1 text-muted font-weight-bold">
|
||||||
<li class="list-inline-item"></li>
|
<li className="list-inline-item"></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -206,13 +207,11 @@ export class PrivateMessageForm extends Component<
|
||||||
wsClient.createPrivateMessage(i.state.privateMessageForm)
|
wsClient.createPrivateMessage(i.state.privateMessageForm)
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
i.state.loading = true;
|
i.setState({ loading: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleContentChange(val: string) {
|
handleContentChange(val: string) {
|
||||||
this.state.privateMessageForm.content = val;
|
this.setState(s => ((s.privateMessageForm.content = val), s));
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancel(i: PrivateMessageForm) {
|
handleCancel(i: PrivateMessageForm) {
|
||||||
|
@ -221,13 +220,11 @@ export class PrivateMessageForm extends Component<
|
||||||
|
|
||||||
handlePreviewToggle(i: PrivateMessageForm, event: any) {
|
handlePreviewToggle(i: PrivateMessageForm, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.previewMode = !i.state.previewMode;
|
i.setState({ previewMode: !i.state.previewMode });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowDisclaimer(i: PrivateMessageForm) {
|
handleShowDisclaimer(i: PrivateMessageForm) {
|
||||||
i.state.showDisclaimer = !i.state.showDisclaimer;
|
i.setState({ showDisclaimer: !i.state.showDisclaimer });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -235,8 +232,7 @@ export class PrivateMessageForm extends Component<
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
this.state.loading = false;
|
this.setState({ loading: false });
|
||||||
this.setState(this.state);
|
|
||||||
return;
|
return;
|
||||||
} else if (
|
} else if (
|
||||||
op == UserOperation.EditPrivateMessage ||
|
op == UserOperation.EditPrivateMessage ||
|
||||||
|
@ -247,16 +243,14 @@ export class PrivateMessageForm extends Component<
|
||||||
msg,
|
msg,
|
||||||
PrivateMessageResponse
|
PrivateMessageResponse
|
||||||
);
|
);
|
||||||
this.state.loading = false;
|
this.setState({ loading: false });
|
||||||
this.props.onEdit(data.private_message_view);
|
this.props.onEdit(data.private_message_view);
|
||||||
} else if (op == UserOperation.CreatePrivateMessage) {
|
} else if (op == UserOperation.CreatePrivateMessage) {
|
||||||
let data = wsJsonToRes<PrivateMessageResponse>(
|
let data = wsJsonToRes<PrivateMessageResponse>(
|
||||||
msg,
|
msg,
|
||||||
PrivateMessageResponse
|
PrivateMessageResponse
|
||||||
);
|
);
|
||||||
this.state.loading = false;
|
|
||||||
this.props.onCreate(data.private_message_view);
|
this.props.onCreate(data.private_message_view);
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
import { Component, linkEvent } from "inferno";
|
||||||
|
import { T } from "inferno-i18next-dess";
|
||||||
|
import {
|
||||||
|
PrivateMessageReportView,
|
||||||
|
ResolvePrivateMessageReport,
|
||||||
|
} from "lemmy-js-client";
|
||||||
|
import { i18n } from "../../i18next";
|
||||||
|
import { WebSocketService } from "../../services";
|
||||||
|
import { auth, mdToHtml, wsClient } from "../../utils";
|
||||||
|
import { Icon } from "../common/icon";
|
||||||
|
import { PersonListing } from "../person/person-listing";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
report: PrivateMessageReportView;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PrivateMessageReport extends Component<Props, any> {
|
||||||
|
constructor(props: any, context: any) {
|
||||||
|
super(props, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let r = this.props.report;
|
||||||
|
let pmr = r.private_message_report;
|
||||||
|
let tippyContent = i18n.t(
|
||||||
|
r.private_message_report.resolved ? "unresolve_report" : "resolve_report"
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
{i18n.t("creator")}:{" "}
|
||||||
|
<PersonListing person={r.private_message_creator} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{i18n.t("message")}:
|
||||||
|
<div
|
||||||
|
className="md-div"
|
||||||
|
dangerouslySetInnerHTML={mdToHtml(pmr.original_pm_text)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{i18n.t("reporter")}: <PersonListing person={r.creator} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{i18n.t("reason")}: {pmr.reason}
|
||||||
|
</div>
|
||||||
|
{r.resolver.match({
|
||||||
|
some: resolver => (
|
||||||
|
<div>
|
||||||
|
{pmr.resolved ? (
|
||||||
|
<T i18nKey="resolved_by">
|
||||||
|
#
|
||||||
|
<PersonListing person={resolver} />
|
||||||
|
</T>
|
||||||
|
) : (
|
||||||
|
<T i18nKey="unresolved_by">
|
||||||
|
#
|
||||||
|
<PersonListing person={resolver} />
|
||||||
|
</T>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
|
<button
|
||||||
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
|
onClick={linkEvent(this, this.handleResolveReport)}
|
||||||
|
data-tippy-content={tippyContent}
|
||||||
|
aria-label={tippyContent}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="check"
|
||||||
|
classes={`icon-inline ${
|
||||||
|
pmr.resolved ? "text-success" : "text-danger"
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleResolveReport(i: PrivateMessageReport) {
|
||||||
|
let pmr = i.props.report.private_message_report;
|
||||||
|
let form = new ResolvePrivateMessageReport({
|
||||||
|
report_id: pmr.id,
|
||||||
|
resolved: !pmr.resolved,
|
||||||
|
auth: auth().unwrap(),
|
||||||
|
});
|
||||||
|
WebSocketService.Instance.send(wsClient.resolvePrivateMessageReport(form));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,12 @@
|
||||||
import { None, Some } from "@sniptt/monads/build";
|
import { None, Option, Some } from "@sniptt/monads/build";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
|
CreatePrivateMessageReport,
|
||||||
DeletePrivateMessage,
|
DeletePrivateMessage,
|
||||||
MarkPrivateMessageAsRead,
|
MarkPrivateMessageAsRead,
|
||||||
PersonSafe,
|
PersonSafe,
|
||||||
PrivateMessageView,
|
PrivateMessageView,
|
||||||
|
toUndefined,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
|
@ -19,6 +21,8 @@ interface PrivateMessageState {
|
||||||
showEdit: boolean;
|
showEdit: boolean;
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
viewSource: boolean;
|
viewSource: boolean;
|
||||||
|
showReportDialog: boolean;
|
||||||
|
reportReason: Option<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PrivateMessageProps {
|
interface PrivateMessageProps {
|
||||||
|
@ -34,6 +38,8 @@ export class PrivateMessage extends Component<
|
||||||
showEdit: false,
|
showEdit: false,
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
viewSource: false,
|
viewSource: false,
|
||||||
|
showReportDialog: false,
|
||||||
|
reportReason: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -63,9 +69,9 @@ export class PrivateMessage extends Component<
|
||||||
: message_view.creator;
|
: message_view.creator;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="border-top border-light">
|
<div className="border-top border-light">
|
||||||
<div>
|
<div>
|
||||||
<ul class="list-inline mb-0 text-muted small">
|
<ul className="list-inline mb-0 text-muted small">
|
||||||
{/* TODO refactor this */}
|
{/* TODO refactor this */}
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
{this.mine ? i18n.t("to") : i18n.t("from")}
|
{this.mine ? i18n.t("to") : i18n.t("from")}
|
||||||
|
@ -114,12 +120,12 @@ export class PrivateMessage extends Component<
|
||||||
dangerouslySetInnerHTML={mdToHtml(this.messageUnlessRemoved)}
|
dangerouslySetInnerHTML={mdToHtml(this.messageUnlessRemoved)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<ul class="list-inline mb-0 text-muted font-weight-bold">
|
<ul className="list-inline mb-0 text-muted font-weight-bold">
|
||||||
{!this.mine && (
|
{!this.mine && (
|
||||||
<>
|
<>
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(this, this.handleMarkRead)}
|
onClick={linkEvent(this, this.handleMarkRead)}
|
||||||
data-tippy-content={
|
data-tippy-content={
|
||||||
message_view.private_message.read
|
message_view.private_message.read
|
||||||
|
@ -140,9 +146,10 @@ export class PrivateMessage extends Component<
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
<li className="list-inline-item">{this.reportButton}</li>
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(this, this.handleReplyClick)}
|
onClick={linkEvent(this, this.handleReplyClick)}
|
||||||
data-tippy-content={i18n.t("reply")}
|
data-tippy-content={i18n.t("reply")}
|
||||||
aria-label={i18n.t("reply")}
|
aria-label={i18n.t("reply")}
|
||||||
|
@ -156,7 +163,7 @@ export class PrivateMessage extends Component<
|
||||||
<>
|
<>
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(this, this.handleEditClick)}
|
onClick={linkEvent(this, this.handleEditClick)}
|
||||||
data-tippy-content={i18n.t("edit")}
|
data-tippy-content={i18n.t("edit")}
|
||||||
aria-label={i18n.t("edit")}
|
aria-label={i18n.t("edit")}
|
||||||
|
@ -166,7 +173,7 @@ export class PrivateMessage extends Component<
|
||||||
</li>
|
</li>
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(this, this.handleDeleteClick)}
|
onClick={linkEvent(this, this.handleDeleteClick)}
|
||||||
data-tippy-content={
|
data-tippy-content={
|
||||||
!message_view.private_message.deleted
|
!message_view.private_message.deleted
|
||||||
|
@ -192,7 +199,7 @@ export class PrivateMessage extends Component<
|
||||||
)}
|
)}
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<button
|
<button
|
||||||
class="btn btn-link btn-animate text-muted"
|
className="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(this, this.handleViewSource)}
|
onClick={linkEvent(this, this.handleViewSource)}
|
||||||
data-tippy-content={i18n.t("view_source")}
|
data-tippy-content={i18n.t("view_source")}
|
||||||
aria-label={i18n.t("view_source")}
|
aria-label={i18n.t("view_source")}
|
||||||
|
@ -209,6 +216,32 @@ export class PrivateMessage extends Component<
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{this.state.showReportDialog && (
|
||||||
|
<form
|
||||||
|
className="form-inline"
|
||||||
|
onSubmit={linkEvent(this, this.handleReportSubmit)}
|
||||||
|
>
|
||||||
|
<label className="sr-only" htmlFor="pm-report-reason">
|
||||||
|
{i18n.t("reason")}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="pm-report-reason"
|
||||||
|
className="form-control mr-2"
|
||||||
|
placeholder={i18n.t("reason")}
|
||||||
|
required
|
||||||
|
value={toUndefined(this.state.reportReason)}
|
||||||
|
onInput={linkEvent(this, this.handleReportReasonChange)}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-secondary"
|
||||||
|
aria-label={i18n.t("create_report")}
|
||||||
|
>
|
||||||
|
{i18n.t("create_report")}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
{this.state.showReply && (
|
{this.state.showReply && (
|
||||||
<PrivateMessageForm
|
<PrivateMessageForm
|
||||||
recipient={otherPerson}
|
recipient={otherPerson}
|
||||||
|
@ -217,23 +250,35 @@ export class PrivateMessage extends Component<
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* A collapsed clearfix */}
|
{/* A collapsed clearfix */}
|
||||||
{this.state.collapsed && <div class="row col-12"></div>}
|
{this.state.collapsed && <div className="row col-12"></div>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get reportButton() {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
|
onClick={linkEvent(this, this.handleShowReportDialog)}
|
||||||
|
data-tippy-content={i18n.t("show_report_dialog")}
|
||||||
|
aria-label={i18n.t("show_report_dialog")}
|
||||||
|
>
|
||||||
|
<Icon icon="flag" inline />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
get messageUnlessRemoved(): string {
|
get messageUnlessRemoved(): string {
|
||||||
let message = this.props.private_message_view.private_message;
|
let message = this.props.private_message_view.private_message;
|
||||||
return message.deleted ? `*${i18n.t("deleted")}*` : message.content;
|
return message.deleted ? `*${i18n.t("deleted")}*` : message.content;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReplyClick(i: PrivateMessage) {
|
handleReplyClick(i: PrivateMessage) {
|
||||||
i.state.showReply = true;
|
i.setState({ showReply: true });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditClick(i: PrivateMessage) {
|
handleEditClick(i: PrivateMessage) {
|
||||||
i.state.showEdit = true;
|
i.setState({ showEdit: true });
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,9 +292,7 @@ export class PrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReplyCancel() {
|
handleReplyCancel() {
|
||||||
this.state.showReply = false;
|
this.setState({ showReply: false, showEdit: false });
|
||||||
this.state.showEdit = false;
|
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMarkRead(i: PrivateMessage) {
|
handleMarkRead(i: PrivateMessage) {
|
||||||
|
@ -262,26 +305,42 @@ export class PrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMessageCollapse(i: PrivateMessage) {
|
handleMessageCollapse(i: PrivateMessage) {
|
||||||
i.state.collapsed = !i.state.collapsed;
|
i.setState({ collapsed: !i.state.collapsed });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleViewSource(i: PrivateMessage) {
|
handleViewSource(i: PrivateMessage) {
|
||||||
i.state.viewSource = !i.state.viewSource;
|
i.setState({ viewSource: !i.state.viewSource });
|
||||||
i.setState(i.state);
|
}
|
||||||
|
|
||||||
|
handleShowReportDialog(i: PrivateMessage) {
|
||||||
|
i.setState({ showReportDialog: !i.state.showReportDialog });
|
||||||
|
}
|
||||||
|
|
||||||
|
handleReportReasonChange(i: PrivateMessage, event: any) {
|
||||||
|
i.setState({ reportReason: Some(event.target.value) });
|
||||||
|
}
|
||||||
|
|
||||||
|
handleReportSubmit(i: PrivateMessage, event: any) {
|
||||||
|
event.preventDefault();
|
||||||
|
let form = new CreatePrivateMessageReport({
|
||||||
|
private_message_id: i.props.private_message_view.private_message.id,
|
||||||
|
reason: toUndefined(i.state.reportReason),
|
||||||
|
auth: auth().unwrap(),
|
||||||
|
});
|
||||||
|
WebSocketService.Instance.send(wsClient.createPrivateMessageReport(form));
|
||||||
|
|
||||||
|
i.setState({ showReportDialog: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePrivateMessageEdit() {
|
handlePrivateMessageEdit() {
|
||||||
this.state.showEdit = false;
|
this.setState({ showEdit: false });
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePrivateMessageCreate(message: PrivateMessageView) {
|
handlePrivateMessageCreate(message: PrivateMessageView) {
|
||||||
UserService.Instance.myUserInfo.match({
|
UserService.Instance.myUserInfo.match({
|
||||||
some: mui => {
|
some: mui => {
|
||||||
if (message.creator.id == mui.local_user_view.person.id) {
|
if (message.creator.id == mui.local_user_view.person.id) {
|
||||||
this.state.showReply = false;
|
this.setState({ showReply: false });
|
||||||
this.setState(this.state);
|
|
||||||
toast(i18n.t("message_sent"));
|
toast(i18n.t("message_sent"));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -200,28 +200,36 @@ export class Search extends Component<any, SearchState> {
|
||||||
);
|
);
|
||||||
|
|
||||||
// This can be single or multiple communities given
|
// This can be single or multiple communities given
|
||||||
communitiesRes.match({
|
if (communitiesRes.isSome()) {
|
||||||
some: res => (this.state.communities = res.communities),
|
this.state = {
|
||||||
none: void 0,
|
...this.state,
|
||||||
});
|
communities: communitiesRes.unwrap().communities,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
communityRes.match({
|
if (communityRes.isSome()) {
|
||||||
some: res => (this.state.communities = [res.community_view]),
|
this.state = {
|
||||||
none: void 0,
|
...this.state,
|
||||||
});
|
communities: [communityRes.unwrap().community_view],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
this.state.creatorDetails = Some(
|
this.state = {
|
||||||
this.isoData.routeData[2] as GetPersonDetailsResponse
|
...this.state,
|
||||||
);
|
creatorDetails: Some(
|
||||||
|
this.isoData.routeData[2] as GetPersonDetailsResponse
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
if (this.state.q != "") {
|
if (this.state.q != "") {
|
||||||
this.state.searchResponse = Some(
|
this.state = {
|
||||||
this.isoData.routeData[3] as SearchResponse
|
...this.state,
|
||||||
);
|
searchResponse: Some(this.isoData.routeData[3] as SearchResponse),
|
||||||
this.state.resolveObjectResponse = Some(
|
resolveObjectResponse: Some(
|
||||||
this.isoData.routeData[4] as ResolveObjectResponse
|
this.isoData.routeData[4] as ResolveObjectResponse
|
||||||
);
|
),
|
||||||
this.state.loading = false;
|
loading: false,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this.search();
|
this.search();
|
||||||
}
|
}
|
||||||
|
@ -382,7 +390,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div className="container">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
|
@ -407,12 +415,12 @@ export class Search extends Component<any, SearchState> {
|
||||||
searchForm() {
|
searchForm() {
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
class="form-inline"
|
className="form-inline"
|
||||||
onSubmit={linkEvent(this, this.handleSearchSubmit)}
|
onSubmit={linkEvent(this, this.handleSearchSubmit)}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control mr-2 mb-2"
|
className="form-control mr-2 mb-2"
|
||||||
value={this.state.searchText}
|
value={this.state.searchText}
|
||||||
placeholder={`${i18n.t("search")}...`}
|
placeholder={`${i18n.t("search")}...`}
|
||||||
aria-label={i18n.t("search")}
|
aria-label={i18n.t("search")}
|
||||||
|
@ -420,7 +428,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
required
|
required
|
||||||
minLength={1}
|
minLength={1}
|
||||||
/>
|
/>
|
||||||
<button type="submit" class="btn btn-secondary mr-2 mb-2">
|
<button type="submit" className="btn btn-secondary mr-2 mb-2">
|
||||||
{this.state.loading ? <Spinner /> : <span>{i18n.t("search")}</span>}
|
{this.state.loading ? <Spinner /> : <span>{i18n.t("search")}</span>}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
@ -433,7 +441,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
<select
|
<select
|
||||||
value={this.state.type_}
|
value={this.state.type_}
|
||||||
onChange={linkEvent(this, this.handleTypeChange)}
|
onChange={linkEvent(this, this.handleTypeChange)}
|
||||||
class="custom-select w-auto mb-2"
|
className="custom-select w-auto mb-2"
|
||||||
aria-label={i18n.t("type")}
|
aria-label={i18n.t("type")}
|
||||||
>
|
>
|
||||||
<option disabled aria-hidden="true">
|
<option disabled aria-hidden="true">
|
||||||
|
@ -448,7 +456,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
<option value={SearchType.Users}>{i18n.t("users")}</option>
|
<option value={SearchType.Users}>{i18n.t("users")}</option>
|
||||||
<option value={SearchType.Url}>{i18n.t("url")}</option>
|
<option value={SearchType.Url}>{i18n.t("url")}</option>
|
||||||
</select>
|
</select>
|
||||||
<span class="ml-2">
|
<span className="ml-2">
|
||||||
<ListingTypeSelect
|
<ListingTypeSelect
|
||||||
type_={this.state.listingType}
|
type_={this.state.listingType}
|
||||||
showLocal={showLocal(this.isoData)}
|
showLocal={showLocal(this.isoData)}
|
||||||
|
@ -456,7 +464,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
onChange={this.handleListingTypeChange}
|
onChange={this.handleListingTypeChange}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="ml-2">
|
<span className="ml-2">
|
||||||
<SortSelect
|
<SortSelect
|
||||||
sort={this.state.sort}
|
sort={this.state.sort}
|
||||||
onChange={this.handleSortChange}
|
onChange={this.handleSortChange}
|
||||||
|
@ -464,7 +472,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
hideMostComments
|
hideMostComments
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<div class="form-row">
|
<div className="form-row">
|
||||||
{this.state.communities.length > 0 && this.communityFilter()}
|
{this.state.communities.length > 0 && this.communityFilter()}
|
||||||
{this.creatorFilter()}
|
{this.creatorFilter()}
|
||||||
</div>
|
</div>
|
||||||
|
@ -577,8 +585,8 @@ export class Search extends Component<any, SearchState> {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{combined.map(i => (
|
{combined.map(i => (
|
||||||
<div class="row">
|
<div key={i.published} className="row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
{i.type_ == "posts" && (
|
{i.type_ == "posts" && (
|
||||||
<PostListing
|
<PostListing
|
||||||
key={(i.data as PostView).post.id}
|
key={(i.data as PostView).post.id}
|
||||||
|
@ -589,6 +597,8 @@ export class Search extends Component<any, SearchState> {
|
||||||
showCommunity
|
showCommunity
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
|
viewOnly
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{i.type_ == "comments" && (
|
{i.type_ == "comments" && (
|
||||||
|
@ -602,19 +612,21 @@ export class Search extends Component<any, SearchState> {
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
viewType={CommentViewType.Flat}
|
viewType={CommentViewType.Flat}
|
||||||
|
viewOnly
|
||||||
moderators={None}
|
moderators={None}
|
||||||
admins={None}
|
admins={None}
|
||||||
maxCommentsShown={None}
|
maxCommentsShown={None}
|
||||||
locked
|
locked
|
||||||
noIndent
|
noIndent
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{i.type_ == "communities" && (
|
{i.type_ == "communities" && (
|
||||||
<div>{this.communityListing(i.data as CommunityView)}</div>
|
<div>{this.communityListing(i.data as CommunityView)}</div>
|
||||||
)}
|
)}
|
||||||
{i.type_ == "users" && (
|
{i.type_ == "users" && (
|
||||||
<div>{this.userListing(i.data as PersonViewSafe)}</div>
|
<div>{this.personListing(i.data as PersonViewSafe)}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -639,12 +651,14 @@ export class Search extends Component<any, SearchState> {
|
||||||
<CommentNodes
|
<CommentNodes
|
||||||
nodes={commentsToFlatNodes(comments)}
|
nodes={commentsToFlatNodes(comments)}
|
||||||
viewType={CommentViewType.Flat}
|
viewType={CommentViewType.Flat}
|
||||||
|
viewOnly
|
||||||
locked
|
locked
|
||||||
noIndent
|
noIndent
|
||||||
moderators={None}
|
moderators={None}
|
||||||
admins={None}
|
admins={None}
|
||||||
maxCommentsShown={None}
|
maxCommentsShown={None}
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -663,17 +677,19 @@ export class Search extends Component<any, SearchState> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{posts.map(post => (
|
{posts.map(pv => (
|
||||||
<div class="row">
|
<div key={pv.post.id} className="row">
|
||||||
<div class="col-12">
|
<div className="col-12">
|
||||||
<PostListing
|
<PostListing
|
||||||
post_view={post}
|
post_view={pv}
|
||||||
showCommunity
|
showCommunity
|
||||||
duplicates={None}
|
duplicates={None}
|
||||||
moderators={None}
|
moderators={None}
|
||||||
admins={None}
|
admins={None}
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
|
viewOnly
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -696,9 +712,9 @@ export class Search extends Component<any, SearchState> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{communities.map(community => (
|
{communities.map(cv => (
|
||||||
<div class="row">
|
<div key={cv.community.id} className="row">
|
||||||
<div class="col-12">{this.communityListing(community)}</div>
|
<div className="col-12">{this.communityListing(cv)}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
@ -719,9 +735,9 @@ export class Search extends Component<any, SearchState> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{users.map(user => (
|
{users.map(pvs => (
|
||||||
<div class="row">
|
<div key={pvs.person.id} className="row">
|
||||||
<div class="col-12">{this.userListing(user)}</div>
|
<div className="col-12">{this.personListing(pvs)}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
@ -744,33 +760,37 @@ export class Search extends Component<any, SearchState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
userListing(person_view: PersonViewSafe) {
|
personListing(person_view: PersonViewSafe) {
|
||||||
return [
|
return (
|
||||||
<span>
|
<>
|
||||||
<PersonListing person={person_view.person} showApubName />
|
<span>
|
||||||
</span>,
|
<PersonListing person={person_view.person} showApubName />
|
||||||
<span>{` - ${i18n.t("number_of_comments", {
|
</span>
|
||||||
count: person_view.counts.comment_count,
|
<span>{` - ${i18n.t("number_of_comments", {
|
||||||
formattedCount: numToSI(person_view.counts.comment_count),
|
count: person_view.counts.comment_count,
|
||||||
})}`}</span>,
|
formattedCount: numToSI(person_view.counts.comment_count),
|
||||||
];
|
})}`}</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
communityFilter() {
|
communityFilter() {
|
||||||
return (
|
return (
|
||||||
<div class="form-group col-sm-6">
|
<div className="form-group col-sm-6">
|
||||||
<label class="col-form-label" htmlFor="community-filter">
|
<label className="col-form-label" htmlFor="community-filter">
|
||||||
{i18n.t("community")}
|
{i18n.t("community")}
|
||||||
</label>
|
</label>
|
||||||
<div>
|
<div>
|
||||||
<select
|
<select
|
||||||
class="form-control"
|
className="form-control"
|
||||||
id="community-filter"
|
id="community-filter"
|
||||||
value={this.state.communityId}
|
value={this.state.communityId}
|
||||||
>
|
>
|
||||||
<option value="0">{i18n.t("all")}</option>
|
<option value="0">{i18n.t("all")}</option>
|
||||||
{this.state.communities.map(cv => (
|
{this.state.communities.map(cv => (
|
||||||
<option value={cv.community.id}>{communitySelectName(cv)}</option>
|
<option key={cv.community.id} value={cv.community.id}>
|
||||||
|
{communitySelectName(cv)}
|
||||||
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -780,13 +800,13 @@ export class Search extends Component<any, SearchState> {
|
||||||
|
|
||||||
creatorFilter() {
|
creatorFilter() {
|
||||||
return (
|
return (
|
||||||
<div class="form-group col-sm-6">
|
<div className="form-group col-sm-6">
|
||||||
<label class="col-form-label" htmlFor="creator-filter">
|
<label className="col-form-label" htmlFor="creator-filter">
|
||||||
{capitalizeFirstLetter(i18n.t("creator"))}
|
{capitalizeFirstLetter(i18n.t("creator"))}
|
||||||
</label>
|
</label>
|
||||||
<div>
|
<div>
|
||||||
<select
|
<select
|
||||||
class="form-control"
|
className="form-control"
|
||||||
id="creator-filter"
|
id="creator-filter"
|
||||||
value={this.state.creatorId}
|
value={this.state.creatorId}
|
||||||
>
|
>
|
||||||
|
@ -852,10 +872,11 @@ export class Search extends Component<any, SearchState> {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.state.q != "") {
|
if (this.state.q != "") {
|
||||||
this.state.searchResponse = None;
|
this.setState({
|
||||||
this.state.resolveObjectResponse = None;
|
searchResponse: None,
|
||||||
this.state.loading = true;
|
resolveObjectResponse: None,
|
||||||
this.setState(this.state);
|
loading: true,
|
||||||
|
});
|
||||||
WebSocketService.Instance.send(wsClient.search(form));
|
WebSocketService.Instance.send(wsClient.search(form));
|
||||||
WebSocketService.Instance.send(wsClient.resolveObject(resolveObjectForm));
|
WebSocketService.Instance.send(wsClient.resolveObject(resolveObjectForm));
|
||||||
}
|
}
|
||||||
|
@ -996,11 +1017,13 @@ 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 = Some({
|
this.setState({
|
||||||
comment: None,
|
resolveObjectResponse: Some({
|
||||||
post: None,
|
comment: None,
|
||||||
community: None,
|
post: None,
|
||||||
person: None,
|
community: None,
|
||||||
|
person: None,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
this.checkFinishedLoading();
|
this.checkFinishedLoading();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1009,7 +1032,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.Search) {
|
} else if (op == UserOperation.Search) {
|
||||||
let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
|
let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
|
||||||
this.state.searchResponse = Some(data);
|
this.setState({ searchResponse: Some(data) });
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.checkFinishedLoading();
|
this.checkFinishedLoading();
|
||||||
restoreScrollPosition(this.context);
|
restoreScrollPosition(this.context);
|
||||||
|
@ -1032,12 +1055,11 @@ export class Search extends Component<any, SearchState> {
|
||||||
msg,
|
msg,
|
||||||
ListCommunitiesResponse
|
ListCommunitiesResponse
|
||||||
);
|
);
|
||||||
this.state.communities = data.communities;
|
this.setState({ communities: data.communities });
|
||||||
this.setState(this.state);
|
|
||||||
this.setupCommunityFilter();
|
this.setupCommunityFilter();
|
||||||
} else if (op == UserOperation.ResolveObject) {
|
} else if (op == UserOperation.ResolveObject) {
|
||||||
let data = wsJsonToRes<ResolveObjectResponse>(msg, ResolveObjectResponse);
|
let data = wsJsonToRes<ResolveObjectResponse>(msg, ResolveObjectResponse);
|
||||||
this.state.resolveObjectResponse = Some(data);
|
this.setState({ resolveObjectResponse: Some(data) });
|
||||||
this.checkFinishedLoading();
|
this.checkFinishedLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1047,8 +1069,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
this.state.searchResponse.isSome() &&
|
this.state.searchResponse.isSome() &&
|
||||||
this.state.resolveObjectResponse.isSome()
|
this.state.resolveObjectResponse.isSome()
|
||||||
) {
|
) {
|
||||||
this.state.loading = false;
|
this.setState({ loading: false });
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { None, Option, Result, Some } from "@sniptt/monads";
|
import { Err, None, Ok, Option, Result, Some } from "@sniptt/monads";
|
||||||
import { ClassConstructor, deserialize, serialize } from "class-transformer";
|
import { ClassConstructor, deserialize, serialize } from "class-transformer";
|
||||||
import emojiShortName from "emoji-short-name";
|
import emojiShortName from "emoji-short-name";
|
||||||
import {
|
import {
|
||||||
|
@ -23,6 +23,7 @@ import {
|
||||||
PersonViewSafe,
|
PersonViewSafe,
|
||||||
PostReportView,
|
PostReportView,
|
||||||
PostView,
|
PostView,
|
||||||
|
PrivateMessageReportView,
|
||||||
PrivateMessageView,
|
PrivateMessageView,
|
||||||
RegistrationApplicationView,
|
RegistrationApplicationView,
|
||||||
Search,
|
Search,
|
||||||
|
@ -69,7 +70,7 @@ export const webArchiveUrl = "https://web.archive.org";
|
||||||
export const elementUrl = "https://element.io";
|
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 = 40;
|
||||||
export const trendingFetchLimit = 6;
|
export const trendingFetchLimit = 6;
|
||||||
export const mentionDropdownFetchLimit = 10;
|
export const mentionDropdownFetchLimit = 10;
|
||||||
export const commentTreeMaxDepth = 8;
|
export const commentTreeMaxDepth = 8;
|
||||||
|
@ -246,14 +247,10 @@ export function isAdmin(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function amAdmin(
|
export function amAdmin(myUserInfo = UserService.Instance.myUserInfo): boolean {
|
||||||
admins: Option<PersonViewSafe[]>,
|
return myUserInfo
|
||||||
myUserInfo = UserService.Instance.myUserInfo
|
.map(mui => mui.local_user_view.person.admin)
|
||||||
): boolean {
|
.unwrapOr(false);
|
||||||
return myUserInfo.match({
|
|
||||||
some: mui => isAdmin(admins, mui.local_user_view.person.id),
|
|
||||||
none: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function amCommunityCreator(
|
export function amCommunityCreator(
|
||||||
|
@ -418,7 +415,7 @@ export function getLanguages(
|
||||||
myUserInfo = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): string[] {
|
): string[] {
|
||||||
let myLang = myUserInfo
|
let myLang = myUserInfo
|
||||||
.map(m => m.local_user_view.local_user.lang)
|
.map(m => m.local_user_view.local_user.interface_language)
|
||||||
.unwrapOr("browser");
|
.unwrapOr("browser");
|
||||||
let lang = override || myLang;
|
let lang = override || myLang;
|
||||||
|
|
||||||
|
@ -638,21 +635,18 @@ export function notifyPrivateMessage(pmv: PrivateMessageView, router: any) {
|
||||||
function notify(info: NotifyInfo, router: any) {
|
function notify(info: NotifyInfo, router: any) {
|
||||||
messageToastify(info, router);
|
messageToastify(info, router);
|
||||||
|
|
||||||
// TODO absolute nightmare bug, but notifs are currently broken.
|
if (Notification.permission !== "granted") Notification.requestPermission();
|
||||||
// Notification.new will try to do a browser fetch ???
|
else {
|
||||||
|
var notification = new Notification(info.name, {
|
||||||
|
...{ body: info.body },
|
||||||
|
...(info.icon.isSome() && { icon: info.icon.unwrap() }),
|
||||||
|
});
|
||||||
|
|
||||||
// if (Notification.permission !== "granted") Notification.requestPermission();
|
notification.onclick = (ev: Event): any => {
|
||||||
// else {
|
ev.preventDefault();
|
||||||
// var notification = new Notification(info.name, {
|
router.history.push(info.link);
|
||||||
// icon: info.icon,
|
};
|
||||||
// body: info.body,
|
}
|
||||||
// });
|
|
||||||
|
|
||||||
// notification.onclick = (ev: Event): any => {
|
|
||||||
// ev.preventDefault();
|
|
||||||
// router.history.push(info.link);
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setupTribute() {
|
export function setupTribute() {
|
||||||
|
@ -959,6 +953,7 @@ export function editPostRes(data: PostView, post: PostView) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO possible to make these generic?
|
||||||
export function updatePostReportRes(
|
export function updatePostReportRes(
|
||||||
data: PostReportView,
|
data: PostReportView,
|
||||||
reports: PostReportView[]
|
reports: PostReportView[]
|
||||||
|
@ -979,6 +974,18 @@ export function updateCommentReportRes(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updatePrivateMessageReportRes(
|
||||||
|
data: PrivateMessageReportView,
|
||||||
|
reports: PrivateMessageReportView[]
|
||||||
|
) {
|
||||||
|
let found = reports.find(
|
||||||
|
c => c.private_message_report.id == data.private_message_report.id
|
||||||
|
);
|
||||||
|
if (found) {
|
||||||
|
found.private_message_report = data.private_message_report;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function updateRegistrationApplicationRes(
|
export function updateRegistrationApplicationRes(
|
||||||
data: RegistrationApplicationView,
|
data: RegistrationApplicationView,
|
||||||
applications: RegistrationApplicationView[]
|
applications: RegistrationApplicationView[]
|
||||||
|
@ -1338,44 +1345,11 @@ export const choicesConfig = {
|
||||||
shouldSort: false,
|
shouldSort: false,
|
||||||
searchResultLimit: fetchLimit,
|
searchResultLimit: fetchLimit,
|
||||||
classNames: {
|
classNames: {
|
||||||
containerOuter: "choices",
|
containerOuter: "choices custom-select px-0",
|
||||||
containerInner: "choices__inner bg-secondary border-0",
|
|
||||||
input: "form-control",
|
|
||||||
inputCloned: "choices__input--cloned",
|
|
||||||
list: "choices__list",
|
|
||||||
listItems: "choices__list--multiple",
|
|
||||||
listSingle: "choices__list--single",
|
|
||||||
listDropdown: "choices__list--dropdown",
|
|
||||||
item: "choices__item bg-secondary",
|
|
||||||
itemSelectable: "choices__item--selectable",
|
|
||||||
itemDisabled: "choices__item--disabled",
|
|
||||||
itemChoice: "choices__item--choice",
|
|
||||||
placeholder: "choices__placeholder",
|
|
||||||
group: "choices__group",
|
|
||||||
groupHeading: "choices__heading",
|
|
||||||
button: "choices__button",
|
|
||||||
activeState: "is-active",
|
|
||||||
focusState: "is-focused",
|
|
||||||
openState: "is-open",
|
|
||||||
disabledState: "is-disabled",
|
|
||||||
highlightedState: "text-info",
|
|
||||||
selectedState: "text-info",
|
|
||||||
flippedState: "is-flipped",
|
|
||||||
loadingState: "is-loading",
|
|
||||||
noResults: "has-no-results",
|
|
||||||
noChoices: "has-no-choices",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const choicesModLogConfig = {
|
|
||||||
shouldSort: false,
|
|
||||||
searchResultLimit: fetchLimit,
|
|
||||||
classNames: {
|
|
||||||
containerOuter: "choices mb-2 custom-select col-4 px-0",
|
|
||||||
containerInner:
|
containerInner:
|
||||||
"choices__inner bg-secondary border-0 py-0 modlog-choices-font-size",
|
"choices__inner bg-secondary border-0 py-0 modlog-choices-font-size",
|
||||||
input: "form-control",
|
input: "form-control",
|
||||||
inputCloned: "choices__input--cloned w-100",
|
inputCloned: "choices__input--cloned",
|
||||||
list: "choices__list",
|
list: "choices__list",
|
||||||
listItems: "choices__list--multiple",
|
listItems: "choices__list--multiple",
|
||||||
listSingle: "choices__list--single py-0",
|
listSingle: "choices__list--single py-0",
|
||||||
|
@ -1472,3 +1446,62 @@ export function postToCommentSortType(sort: SortType): CommentSortType {
|
||||||
return CommentSortType.Top;
|
return CommentSortType.Top;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function arrayGet<T>(arr: Array<T>, index: number): Result<T, string> {
|
||||||
|
let out = arr.at(index);
|
||||||
|
if (out == undefined) {
|
||||||
|
return Err("Index undefined");
|
||||||
|
} else {
|
||||||
|
return Ok(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function myFirstDiscussionLanguageId(
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
|
): Option<number> {
|
||||||
|
return myUserInfo.andThen(mui =>
|
||||||
|
arrayGet(mui.discussion_languages, 0)
|
||||||
|
.ok()
|
||||||
|
.map(i => i.id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canCreateCommunity(
|
||||||
|
siteRes: GetSiteResponse,
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
|
): boolean {
|
||||||
|
let adminOnly = siteRes.site_view
|
||||||
|
.map(s => s.site.community_creation_admin_only)
|
||||||
|
.unwrapOr(false);
|
||||||
|
return !adminOnly || amAdmin(myUserInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPostBlocked(
|
||||||
|
pv: PostView,
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
|
): boolean {
|
||||||
|
return myUserInfo
|
||||||
|
.map(
|
||||||
|
mui =>
|
||||||
|
mui.community_blocks
|
||||||
|
.map(c => c.community.id)
|
||||||
|
.includes(pv.community.id) ||
|
||||||
|
mui.person_blocks.map(p => p.target.id).includes(pv.creator.id)
|
||||||
|
)
|
||||||
|
.unwrapOr(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks to make sure you can view NSFW posts. Returns true if you can.
|
||||||
|
export function nsfwCheck(
|
||||||
|
pv: PostView,
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
|
): boolean {
|
||||||
|
let nsfw = pv.post.nsfw || pv.community.nsfw;
|
||||||
|
return (
|
||||||
|
!nsfw ||
|
||||||
|
(nsfw &&
|
||||||
|
myUserInfo
|
||||||
|
.map(m => m.local_user_view.local_user.show_nsfw)
|
||||||
|
.unwrapOr(false))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue