Merge branch 'main' into fix-nsfw-blur-spill
This commit is contained in:
commit
1088ee293b
16 changed files with 208 additions and 101 deletions
21
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
21
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
|
@ -9,6 +9,19 @@ body:
|
||||||
Found a bug? Please fill out the sections below. 👍
|
Found a bug? Please fill out the sections below. 👍
|
||||||
Thanks for taking the time to fill out this bug report!
|
Thanks for taking the time to fill out this bug report!
|
||||||
For backend issues, use [lemmy](https://github.com/LemmyNet/lemmy)
|
For backend issues, use [lemmy](https://github.com/LemmyNet/lemmy)
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Requirements
|
||||||
|
description: Before you create a bug report please do the following.
|
||||||
|
options:
|
||||||
|
- label: Is this a bug report? For questions or discussions use https://lemmy.ml/c/lemmy_support
|
||||||
|
required: true
|
||||||
|
- label: Did you check to see if this issue already exists?
|
||||||
|
required: true
|
||||||
|
- label: Is this only a single bug? Do not put multiple bugs in one issue.
|
||||||
|
required: true
|
||||||
|
- label: Is this a server side (not related to the UI) issue? Use the [Lemmy back end](https://github.com/LemmyNet/lemmy) repo.
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: summary
|
id: summary
|
||||||
attributes:
|
attributes:
|
||||||
|
@ -22,7 +35,7 @@ body:
|
||||||
label: Steps to Reproduce
|
label: Steps to Reproduce
|
||||||
description: |
|
description: |
|
||||||
Describe the steps to reproduce the bug.
|
Describe the steps to reproduce the bug.
|
||||||
The better your description is _(go 'here', click 'there'...)_ the fastest you'll get an _(accurate)_ resolution.
|
The better your description is _(go 'here', click 'there'...)_ the fastest you'll get an _(accurate)_ resolution.
|
||||||
value: |
|
value: |
|
||||||
1.
|
1.
|
||||||
2.
|
2.
|
||||||
|
@ -45,3 +58,9 @@ body:
|
||||||
placeholder: ex. 0.17.4-rc.4
|
placeholder: ex. 0.17.4-rc.4
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: lemmy-instance
|
||||||
|
attributes:
|
||||||
|
label: Lemmy Instance URL
|
||||||
|
description: Which Lemmy instance do you use? The address
|
||||||
|
placeholder: lemmy.ml, lemmy.world, etc
|
||||||
|
|
13
.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml
vendored
13
.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml
vendored
|
@ -7,6 +7,19 @@ body:
|
||||||
value: |
|
value: |
|
||||||
Have a suggestion about Lemmy's UI?
|
Have a suggestion about Lemmy's UI?
|
||||||
For backend issues, use [lemmy](https://github.com/LemmyNet/lemmy)
|
For backend issues, use [lemmy](https://github.com/LemmyNet/lemmy)
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Requirements
|
||||||
|
description: Before you create a bug report please do the following.
|
||||||
|
options:
|
||||||
|
- label: Is this a feature request? For questions or discussions use https://lemmy.ml/c/lemmy_support
|
||||||
|
required: true
|
||||||
|
- label: Did you check to see if this issue already exists?
|
||||||
|
required: true
|
||||||
|
- label: Is this only a feature request? Do not put multiple feature requests in one issue.
|
||||||
|
required: true
|
||||||
|
- label: Is this a server side (not related to the UI) issue? Use the [Lemmy back end](https://github.com/LemmyNet/lemmy) repo.
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: problem
|
id: problem
|
||||||
attributes:
|
attributes:
|
||||||
|
|
2
.github/ISSUE_TEMPLATE/QUESTION.yml
vendored
2
.github/ISSUE_TEMPLATE/QUESTION.yml
vendored
|
@ -14,4 +14,4 @@ body:
|
||||||
label: Question
|
label: Question
|
||||||
description: What's the question you have about Lemmy's UI?
|
description: What's the question you have about Lemmy's UI?
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
2
.github/ISSUE_TEMPLATE/hexbear.yml
vendored
2
.github/ISSUE_TEMPLATE/hexbear.yml
vendored
|
@ -8,4 +8,4 @@ body:
|
||||||
label: Question
|
label: Question
|
||||||
description: What's the question you have about hexbear?
|
description: What's the question you have about hexbear?
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "lemmy-ui",
|
"name": "lemmy-ui",
|
||||||
"version": "0.18.0-beta.6",
|
"version": "0.18.0-rc.1",
|
||||||
"description": "An isomorphic UI for lemmy",
|
"description": "An isomorphic UI for lemmy",
|
||||||
"repository": "https://github.com/LemmyNet/lemmy-ui",
|
"repository": "https://github.com/LemmyNet/lemmy-ui",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
|
|
|
@ -75,6 +75,11 @@
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.md-div pre {
|
||||||
|
white-space: pre;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.md-div table {
|
.md-div table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -213,6 +218,11 @@ blockquote {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comments {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.thumbnail {
|
.thumbnail {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
min-height: 60px;
|
min-height: 60px;
|
||||||
|
|
|
@ -156,7 +156,7 @@ server.get("/*", async (req, res) => {
|
||||||
site = try_site.data;
|
site = try_site.data;
|
||||||
initializeSite(site);
|
initializeSite(site);
|
||||||
|
|
||||||
if (path != "/setup" && !site.site_view.local_site.site_setup) {
|
if (path !== "/setup" && !site.site_view.local_site.site_setup) {
|
||||||
return res.redirect("/setup");
|
return res.redirect("/setup");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,10 @@ import {
|
||||||
isBrowser,
|
isBrowser,
|
||||||
myAuth,
|
myAuth,
|
||||||
numToSI,
|
numToSI,
|
||||||
|
poll,
|
||||||
showAvatars,
|
showAvatars,
|
||||||
toast,
|
toast,
|
||||||
|
updateUnreadCountsInterval,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { Icon } from "../common/icon";
|
import { Icon } from "../common/icon";
|
||||||
import { PictrsImage } from "../common/pictrs-image";
|
import { PictrsImage } from "../common/pictrs-image";
|
||||||
|
@ -64,7 +66,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
// On the first load, check the unreads
|
// On the first load, check the unreads
|
||||||
this.requestNotificationPermission();
|
this.requestNotificationPermission();
|
||||||
await this.fetchUnreads();
|
this.fetchUnreads();
|
||||||
this.requestNotificationPermission();
|
this.requestNotificationPermission();
|
||||||
|
|
||||||
document.addEventListener("mouseup", this.handleOutsideMenuClick);
|
document.addEventListener("mouseup", this.handleOutsideMenuClick);
|
||||||
|
@ -406,35 +408,36 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
return amAdmin() || moderatesS;
|
return amAdmin() || moderatesS;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchUnreads() {
|
fetchUnreads() {
|
||||||
const auth = myAuth();
|
poll(async () => {
|
||||||
if (auth) {
|
if (window.document.visibilityState !== "hidden") {
|
||||||
this.setState({ unreadInboxCountRes: { state: "loading" } });
|
const auth = myAuth();
|
||||||
this.setState({
|
if (auth) {
|
||||||
unreadInboxCountRes: await HttpService.client.getUnreadCount({
|
this.setState({
|
||||||
auth,
|
unreadInboxCountRes: await HttpService.client.getUnreadCount({
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.moderatesSomething) {
|
|
||||||
this.setState({ unreadReportCountRes: { state: "loading" } });
|
|
||||||
this.setState({
|
|
||||||
unreadReportCountRes: await HttpService.client.getReportCount({
|
|
||||||
auth,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (amAdmin()) {
|
|
||||||
this.setState({ unreadApplicationCountRes: { state: "loading" } });
|
|
||||||
this.setState({
|
|
||||||
unreadApplicationCountRes:
|
|
||||||
await HttpService.client.getUnreadRegistrationApplicationCount({
|
|
||||||
auth,
|
auth,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.moderatesSomething) {
|
||||||
|
this.setState({
|
||||||
|
unreadReportCountRes: await HttpService.client.getReportCount({
|
||||||
|
auth,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amAdmin()) {
|
||||||
|
this.setState({
|
||||||
|
unreadApplicationCountRes:
|
||||||
|
await HttpService.client.getUnreadRegistrationApplicationCount({
|
||||||
|
auth,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}, updateUnreadCountsInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
get unreadInboxCount(): number {
|
get unreadInboxCount(): number {
|
||||||
|
|
|
@ -270,9 +270,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
this.props.moderators
|
this.props.moderators
|
||||||
);
|
);
|
||||||
|
|
||||||
const borderColor = this.props.node.depth
|
|
||||||
? colorList[(this.props.node.depth - 1) % colorList.length]
|
|
||||||
: colorList[0];
|
|
||||||
const moreRepliesBorderColor = this.props.node.depth
|
const moreRepliesBorderColor = this.props.node.depth
|
||||||
? colorList[this.props.node.depth % colorList.length]
|
? colorList[this.props.node.depth % colorList.length]
|
||||||
: colorList[0];
|
: colorList[0];
|
||||||
|
@ -284,26 +281,17 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
node.comment_view.counts.child_count > 0;
|
node.comment_view.counts.child_count > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<li className="comment" role="comment">
|
||||||
className={`comment ${
|
|
||||||
this.props.node.depth && !this.props.noIndent ? "ml-1" : ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
id={`comment-${cv.comment.id}`}
|
id={`comment-${cv.comment.id}`}
|
||||||
className={classNames(`details comment-node py-2`, {
|
className={classNames(`details comment-node py-2`, {
|
||||||
"border-top border-light": !this.props.noBorder,
|
"border-top border-light": !this.props.noBorder,
|
||||||
mark: this.isCommentNew || this.commentView.comment.distinguished,
|
mark: this.isCommentNew || this.commentView.comment.distinguished,
|
||||||
})}
|
})}
|
||||||
style={
|
|
||||||
!this.props.noIndent && this.props.node.depth
|
|
||||||
? `border-left: 2px ${borderColor} solid !important`
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames({
|
||||||
"ml-2": !this.props.noIndent && this.props.node.depth,
|
"ml-2": !this.props.noIndent,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className="d-flex flex-wrap align-items-center text-muted small">
|
<div className="d-flex flex-wrap align-items-center text-muted small">
|
||||||
|
@ -959,9 +947,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
</div>
|
</div>
|
||||||
{showMoreChildren && (
|
{showMoreChildren && (
|
||||||
<div
|
<div
|
||||||
className={`details ml-1 comment-node py-2 ${
|
className={classNames("details ml-1 comment-node py-2", {
|
||||||
!this.props.noBorder ? "border-top border-light" : ""
|
"border-top border-light": !this.props.noBorder,
|
||||||
}`}
|
})}
|
||||||
style={`border-left: 2px ${moreRepliesBorderColor} solid !important`}
|
style={`border-left: 2px ${moreRepliesBorderColor} solid !important`}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
|
@ -1169,6 +1157,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
siteLanguages={this.props.siteLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
hideImages={this.props.hideImages}
|
hideImages={this.props.hideImages}
|
||||||
|
isChild={!this.props.noIndent}
|
||||||
|
depth={this.props.node.depth + 1}
|
||||||
finished={this.props.finished}
|
finished={this.props.finished}
|
||||||
onCommentReplyRead={this.props.onCommentReplyRead}
|
onCommentReplyRead={this.props.onCommentReplyRead}
|
||||||
onPersonMentionRead={this.props.onPersonMentionRead}
|
onPersonMentionRead={this.props.onPersonMentionRead}
|
||||||
|
@ -1192,8 +1182,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* A collapsed clearfix */}
|
{/* A collapsed clearfix */}
|
||||||
{this.state.collapsed && <div className="row col-12"></div>}
|
{this.state.collapsed && <div className="row col-12" />}
|
||||||
</div>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1211,6 +1201,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
|
||||||
|
|
||||||
linkBtn(small = false) {
|
linkBtn(small = false) {
|
||||||
const cv = this.commentView;
|
const cv = this.commentView;
|
||||||
|
|
||||||
const classnames = classNames("btn btn-link btn-animate text-muted", {
|
const classnames = classNames("btn btn-link btn-animate text-muted", {
|
||||||
"btn-sm": small,
|
"btn-sm": small,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import classNames from "classnames";
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import {
|
import {
|
||||||
AddAdmin,
|
AddAdmin,
|
||||||
|
@ -25,6 +26,7 @@ import {
|
||||||
TransferCommunity,
|
TransferCommunity,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { CommentNodeI, CommentViewType } from "../../interfaces";
|
import { CommentNodeI, CommentViewType } from "../../interfaces";
|
||||||
|
import { colorList } from "../../utils";
|
||||||
import { CommentNode } from "./comment-node";
|
import { CommentNode } from "./comment-node";
|
||||||
|
|
||||||
interface CommentNodesProps {
|
interface CommentNodesProps {
|
||||||
|
@ -44,6 +46,8 @@ interface CommentNodesProps {
|
||||||
allLanguages: Language[];
|
allLanguages: Language[];
|
||||||
siteLanguages: number[];
|
siteLanguages: number[];
|
||||||
hideImages?: boolean;
|
hideImages?: boolean;
|
||||||
|
isChild?: boolean;
|
||||||
|
depth?: number;
|
||||||
finished: Map<CommentId, boolean | undefined>;
|
finished: Map<CommentId, boolean | undefined>;
|
||||||
onSaveComment(form: SaveComment): void;
|
onSaveComment(form: SaveComment): void;
|
||||||
onCommentReplyRead(form: MarkCommentReplyAsRead): void;
|
onCommentReplyRead(form: MarkCommentReplyAsRead): void;
|
||||||
|
@ -74,49 +78,61 @@ export class CommentNodes extends Component<CommentNodesProps, any> {
|
||||||
render() {
|
render() {
|
||||||
const maxComments = this.props.maxCommentsShown ?? this.props.nodes.length;
|
const maxComments = this.props.maxCommentsShown ?? this.props.nodes.length;
|
||||||
|
|
||||||
|
const borderColor = this.props.depth
|
||||||
|
? colorList[this.props.depth % colorList.length]
|
||||||
|
: colorList[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="comments">
|
this.props.nodes.length > 0 && (
|
||||||
{this.props.nodes.slice(0, maxComments).map(node => (
|
<ul
|
||||||
<CommentNode
|
className={classNames("comments", {
|
||||||
key={node.comment_view.comment.id}
|
"ms-1": !!this.props.isChild,
|
||||||
node={node}
|
"border-top border-light": !this.props.noBorder,
|
||||||
noBorder={this.props.noBorder}
|
})}
|
||||||
noIndent={this.props.noIndent}
|
style={`border-left: 2px solid ${borderColor} !important;`}
|
||||||
viewOnly={this.props.viewOnly}
|
>
|
||||||
locked={this.props.locked}
|
{this.props.nodes.slice(0, maxComments).map(node => (
|
||||||
moderators={this.props.moderators}
|
<CommentNode
|
||||||
admins={this.props.admins}
|
key={node.comment_view.comment.id}
|
||||||
markable={this.props.markable}
|
node={node}
|
||||||
showContext={this.props.showContext}
|
noBorder={this.props.noBorder}
|
||||||
showCommunity={this.props.showCommunity}
|
noIndent={this.props.noIndent}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
viewOnly={this.props.viewOnly}
|
||||||
viewType={this.props.viewType}
|
locked={this.props.locked}
|
||||||
allLanguages={this.props.allLanguages}
|
moderators={this.props.moderators}
|
||||||
siteLanguages={this.props.siteLanguages}
|
admins={this.props.admins}
|
||||||
hideImages={this.props.hideImages}
|
markable={this.props.markable}
|
||||||
onCommentReplyRead={this.props.onCommentReplyRead}
|
showContext={this.props.showContext}
|
||||||
onPersonMentionRead={this.props.onPersonMentionRead}
|
showCommunity={this.props.showCommunity}
|
||||||
finished={this.props.finished}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
onCreateComment={this.props.onCreateComment}
|
viewType={this.props.viewType}
|
||||||
onEditComment={this.props.onEditComment}
|
allLanguages={this.props.allLanguages}
|
||||||
onCommentVote={this.props.onCommentVote}
|
siteLanguages={this.props.siteLanguages}
|
||||||
onBlockPerson={this.props.onBlockPerson}
|
hideImages={this.props.hideImages}
|
||||||
onSaveComment={this.props.onSaveComment}
|
onCommentReplyRead={this.props.onCommentReplyRead}
|
||||||
onDeleteComment={this.props.onDeleteComment}
|
onPersonMentionRead={this.props.onPersonMentionRead}
|
||||||
onRemoveComment={this.props.onRemoveComment}
|
finished={this.props.finished}
|
||||||
onDistinguishComment={this.props.onDistinguishComment}
|
onCreateComment={this.props.onCreateComment}
|
||||||
onAddModToCommunity={this.props.onAddModToCommunity}
|
onEditComment={this.props.onEditComment}
|
||||||
onAddAdmin={this.props.onAddAdmin}
|
onCommentVote={this.props.onCommentVote}
|
||||||
onBanPersonFromCommunity={this.props.onBanPersonFromCommunity}
|
onBlockPerson={this.props.onBlockPerson}
|
||||||
onBanPerson={this.props.onBanPerson}
|
onSaveComment={this.props.onSaveComment}
|
||||||
onTransferCommunity={this.props.onTransferCommunity}
|
onDeleteComment={this.props.onDeleteComment}
|
||||||
onFetchChildren={this.props.onFetchChildren}
|
onRemoveComment={this.props.onRemoveComment}
|
||||||
onCommentReport={this.props.onCommentReport}
|
onDistinguishComment={this.props.onDistinguishComment}
|
||||||
onPurgePerson={this.props.onPurgePerson}
|
onAddModToCommunity={this.props.onAddModToCommunity}
|
||||||
onPurgeComment={this.props.onPurgeComment}
|
onAddAdmin={this.props.onAddAdmin}
|
||||||
/>
|
onBanPersonFromCommunity={this.props.onBanPersonFromCommunity}
|
||||||
))}
|
onBanPerson={this.props.onBanPerson}
|
||||||
</div>
|
onTransferCommunity={this.props.onTransferCommunity}
|
||||||
|
onFetchChildren={this.props.onFetchChildren}
|
||||||
|
onCommentReport={this.props.onCommentReport}
|
||||||
|
onPurgePerson={this.props.onPurgePerson}
|
||||||
|
onPurgeComment={this.props.onPurgeComment}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,7 @@ export class Login extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLoginUsernameChange(i: Login, event: any) {
|
handleLoginUsernameChange(i: Login, event: any) {
|
||||||
i.state.form.username_or_email = event.target.value;
|
i.state.form.username_or_email = event.target.value.trim();
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -221,7 +221,7 @@ export class Setup extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegisterUsernameChange(i: Setup, event: any) {
|
handleRegisterUsernameChange(i: Setup, event: any) {
|
||||||
i.state.form.username = event.target.value;
|
i.state.form.username = event.target.value.trim();
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -496,7 +496,7 @@ export class Signup extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegisterUsernameChange(i: Signup, event: any) {
|
handleRegisterUsernameChange(i: Signup, event: any) {
|
||||||
i.state.form.username = event.target.value;
|
i.state.form.username = event.target.value.trim();
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ import {
|
||||||
isImage,
|
isImage,
|
||||||
myAuth,
|
myAuth,
|
||||||
myAuthRequired,
|
myAuthRequired,
|
||||||
pictrsDeleteToast,
|
|
||||||
relTags,
|
relTags,
|
||||||
setupTippy,
|
setupTippy,
|
||||||
toast,
|
toast,
|
||||||
|
@ -73,6 +72,7 @@ interface PostFormState {
|
||||||
suggestedPostsRes: RequestState<SearchResponse>;
|
suggestedPostsRes: RequestState<SearchResponse>;
|
||||||
metadataRes: RequestState<GetSiteMetadataResponse>;
|
metadataRes: RequestState<GetSiteMetadataResponse>;
|
||||||
imageLoading: boolean;
|
imageLoading: boolean;
|
||||||
|
imageDeleteUrl: string;
|
||||||
communitySearchLoading: boolean;
|
communitySearchLoading: boolean;
|
||||||
communitySearchOptions: Choice[];
|
communitySearchOptions: Choice[];
|
||||||
previewMode: boolean;
|
previewMode: boolean;
|
||||||
|
@ -86,6 +86,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
form: {},
|
form: {},
|
||||||
loading: false,
|
loading: false,
|
||||||
imageLoading: false,
|
imageLoading: false,
|
||||||
|
imageDeleteUrl: "",
|
||||||
communitySearchLoading: false,
|
communitySearchLoading: false,
|
||||||
previewMode: false,
|
previewMode: false,
|
||||||
communitySearchOptions: [],
|
communitySearchOptions: [],
|
||||||
|
@ -269,6 +270,17 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
{url && isImage(url) && (
|
{url && isImage(url) && (
|
||||||
<img src={url} className="img-fluid" alt="" />
|
<img src={url} className="img-fluid" alt="" />
|
||||||
)}
|
)}
|
||||||
|
{this.state.imageDeleteUrl && (
|
||||||
|
<button
|
||||||
|
className="btn btn-danger btn-sm mt-2"
|
||||||
|
onClick={linkEvent(this, this.handleImageDelete)}
|
||||||
|
aria-label={i18n.t("delete")}
|
||||||
|
data-tippy-content={i18n.t("delete")}
|
||||||
|
>
|
||||||
|
<Icon icon="x" classes="icon-inline mr-1" />
|
||||||
|
{capitalizeFirstLetter(i18n.t("delete"))}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
{this.props.crossPosts && this.props.crossPosts.length > 0 && (
|
{this.props.crossPosts && this.props.crossPosts.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<div className="my-1 text-muted small font-weight-bold">
|
<div className="my-1 text-muted small font-weight-bold">
|
||||||
|
@ -553,7 +565,15 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostUrlChange(i: PostForm, event: any) {
|
handlePostUrlChange(i: PostForm, event: any) {
|
||||||
i.setState(s => ((s.form.url = event.target.value), s));
|
const url = event.target.value;
|
||||||
|
|
||||||
|
i.setState({
|
||||||
|
form: {
|
||||||
|
url,
|
||||||
|
},
|
||||||
|
imageDeleteUrl: "",
|
||||||
|
});
|
||||||
|
|
||||||
i.fetchPageTitle();
|
i.fetchPageTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -644,18 +664,35 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
if (res.state === "success") {
|
if (res.state === "success") {
|
||||||
if (res.data.msg === "ok") {
|
if (res.data.msg === "ok") {
|
||||||
i.state.form.url = res.data.url;
|
i.state.form.url = res.data.url;
|
||||||
pictrsDeleteToast(file.name, res.data.delete_url as string);
|
i.setState({
|
||||||
i.setState({ imageLoading: false });
|
imageLoading: false,
|
||||||
|
imageDeleteUrl: res.data.delete_url as string,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
toast(JSON.stringify(res), "danger");
|
toast(JSON.stringify(res), "danger");
|
||||||
}
|
}
|
||||||
} else if (res.state === "failed") {
|
} else if (res.state === "failed") {
|
||||||
console.error(res.msg);
|
console.error(res.msg);
|
||||||
toast(res.msg, "danger");
|
toast(res.msg, "danger");
|
||||||
|
i.setState({ imageLoading: false });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleImageDelete(i: PostForm) {
|
||||||
|
const { imageDeleteUrl } = i.state;
|
||||||
|
|
||||||
|
fetch(imageDeleteUrl);
|
||||||
|
|
||||||
|
i.setState({
|
||||||
|
imageDeleteUrl: "",
|
||||||
|
imageLoading: false,
|
||||||
|
form: {
|
||||||
|
url: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
handleCommunitySearch = debounce(async (text: string) => {
|
handleCommunitySearch = debounce(async (text: string) => {
|
||||||
const { selectedCommunityChoice } = this.props;
|
const { selectedCommunityChoice } = this.props;
|
||||||
this.setState({ communitySearchLoading: true });
|
this.setState({ communitySearchLoading: true });
|
||||||
|
|
|
@ -835,6 +835,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
search: "",
|
search: "",
|
||||||
}}
|
}}
|
||||||
title={i18n.t("cross_post")}
|
title={i18n.t("cross_post")}
|
||||||
|
data-tippy-content={i18n.t("cross_post")}
|
||||||
|
aria-label={i18n.t("cross_post")}
|
||||||
>
|
>
|
||||||
<Icon icon="copy" inline />
|
<Icon icon="copy" inline />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -75,6 +75,7 @@ export const commentTreeMaxDepth = 8;
|
||||||
export const markdownFieldCharacterLimit = 50000;
|
export const markdownFieldCharacterLimit = 50000;
|
||||||
export const maxUploadImages = 20;
|
export const maxUploadImages = 20;
|
||||||
export const concurrentImageUpload = 4;
|
export const concurrentImageUpload = 4;
|
||||||
|
export const updateUnreadCountsInterval = 30000;
|
||||||
|
|
||||||
export const relTags = "noopener nofollow";
|
export const relTags = "noopener nofollow";
|
||||||
|
|
||||||
|
@ -1127,7 +1128,7 @@ export const colorList: string[] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
function hsl(num: number) {
|
function hsl(num: number) {
|
||||||
return `hsla(${num}, 35%, 50%, 1)`;
|
return `hsla(${num}, 35%, 50%, 0.5)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hostname(url: string): string {
|
export function hostname(url: string): string {
|
||||||
|
@ -1490,3 +1491,18 @@ export function newVote(voteType: VoteType, myVote?: number): number {
|
||||||
return myVote == -1 ? 0 : -1;
|
return myVote == -1 ? 0 : -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sleep(millis: number): Promise<void> {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, millis));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Polls / repeatedly runs a promise, every X milliseconds
|
||||||
|
*/
|
||||||
|
export async function poll(promiseFn: any, millis: number) {
|
||||||
|
if (window.document.visibilityState !== "hidden") {
|
||||||
|
await promiseFn();
|
||||||
|
}
|
||||||
|
await sleep(millis);
|
||||||
|
return poll(promiseFn, millis);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue