summaryrefslogtreecommitdiffstats
path: root/ui/src/components
diff options
context:
space:
mode:
authorDessalines <tyhou13@gmx.com>2020-06-23 21:11:38 -0400
committerDessalines <tyhou13@gmx.com>2020-06-23 21:11:38 -0400
commitdc94e58cbf7e7de10d97331a3056380a3416e0b0 (patch)
tree85ed25783b0470ead3012a9718aea50b39c940dd /ui/src/components
parentfd6a040568239d2e6949394fdc0ce0f7ac70275c (diff)
parent790b944031f9433be765936763d848ffa6e1b496 (diff)
Merge branch 'master' into federation_merge_from_master_2
Diffstat (limited to 'ui/src/components')
-rw-r--r--ui/src/components/comment-form.tsx90
-rw-r--r--ui/src/components/comment-node.tsx47
-rw-r--r--ui/src/components/community-form.tsx6
-rw-r--r--ui/src/components/inbox.tsx20
-rw-r--r--ui/src/components/login.tsx1
-rw-r--r--ui/src/components/navbar.tsx4
-rw-r--r--ui/src/components/post-form.tsx42
-rw-r--r--ui/src/components/post-listing.tsx10
-rw-r--r--ui/src/components/private-message-form.tsx8
-rw-r--r--ui/src/components/private-message.tsx9
-rw-r--r--ui/src/components/site-form.tsx8
-rw-r--r--ui/src/components/user-listing.tsx4
-rw-r--r--ui/src/components/user.tsx25
13 files changed, 172 insertions, 102 deletions
diff --git a/ui/src/components/comment-form.tsx b/ui/src/components/comment-form.tsx
index b3c1a9a1..0fb7824e 100644
--- a/ui/src/components/comment-form.tsx
+++ b/ui/src/components/comment-form.tsx
@@ -18,6 +18,7 @@ import {
setupTribute,
wsJsonToRes,
emojiPicker,
+ pictrsDeleteToast,
} from '../utils';
import { WebSocketService, UserService } from '../services';
import autosize from 'autosize';
@@ -60,7 +61,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
buttonTitle: !this.props.node
? capitalizeFirstLetter(i18n.t('post'))
: this.props.edit
- ? capitalizeFirstLetter(i18n.t('edit'))
+ ? capitalizeFirstLetter(i18n.t('save'))
: capitalizeFirstLetter(i18n.t('reply')),
previewMode: false,
loading: false,
@@ -137,7 +138,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
/>
{this.state.previewMode && (
<div
- className="md-div"
+ className="card card-body md-div"
dangerouslySetInnerHTML={mdToHtml(
this.state.commentForm.content
)}
@@ -150,7 +151,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
<button
type="submit"
class="btn btn-sm btn-secondary mr-2"
- disabled={this.props.disabled}
+ disabled={this.props.disabled || this.state.loading}
>
{this.state.loading ? (
<svg class="icon icon-spinner spin">
@@ -244,18 +245,32 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
});
}
- handleFinished() {
- this.state.previewMode = false;
- this.state.loading = false;
- this.state.commentForm.content = '';
- this.setState(this.state);
- let form: any = document.getElementById(this.formId);
- form.reset();
- if (this.props.node) {
- this.props.onReplyCancel();
+ handleFinished(data: CommentResponse) {
+ let isReply =
+ this.props.node !== undefined && data.comment.parent_id !== null;
+ let xor =
+ +!(data.comment.parent_id !== null) ^ +(this.props.node !== undefined);
+
+ if (
+ (data.comment.creator_id == UserService.Instance.user.id &&
+ // If its a reply, make sure parent child match
+ isReply &&
+ data.comment.parent_id == this.props.node.comment.id) ||
+ // Otherwise, check the XOR of the two
+ (!isReply && xor)
+ ) {
+ this.state.previewMode = false;
+ this.state.loading = false;
+ this.state.commentForm.content = '';
+ this.setState(this.state);
+ let form: any = document.getElementById(this.formId);
+ form.reset();
+ if (this.props.node) {
+ this.props.onReplyCancel();
+ }
+ autosize.update(form);
+ this.setState(this.state);
}
- autosize.update(document.querySelector('textarea'));
- this.setState(this.state);
}
handleCommentSubmit(i: CommentForm, event: any) {
@@ -305,9 +320,9 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
file = event;
}
- const imageUploadUrl = `/pictshare/api/upload.php`;
+ const imageUploadUrl = `/pictrs/image`;
const formData = new FormData();
- formData.append('file', file);
+ formData.append('images[]', file);
i.state.imageLoading = true;
i.setState(i.state);
@@ -318,16 +333,31 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
})
.then(res => res.json())
.then(res => {
- let url = `${window.location.origin}/pictshare/${res.url}`;
- let imageMarkdown =
- res.filetype == 'mp4' ? `[vid](${url}/raw)` : `![](${url})`;
- let content = i.state.commentForm.content;
- content = content ? `${content}\n${imageMarkdown}` : imageMarkdown;
- i.state.commentForm.content = content;
- i.state.imageLoading = false;
- i.setState(i.state);
- let textarea: any = document.getElementById(i.id);
- autosize.update(textarea);
+ console.log('pictrs upload:');
+ console.log(res);
+ if (res.msg == 'ok') {
+ let hash = res.files[0].file;
+ let url = `${window.location.origin}/pictrs/image/${hash}`;
+ let deleteToken = res.files[0].delete_token;
+ let deleteUrl = `${window.location.origin}/pictrs/image/delete/${deleteToken}/${hash}`;
+ let imageMarkdown = `![](${url})`;
+ let content = i.state.commentForm.content;
+ content = content ? `${content}\n${imageMarkdown}` : imageMarkdown;
+ i.state.commentForm.content = content;
+ i.state.imageLoading = false;
+ i.setState(i.state);
+ let textarea: any = document.getElementById(i.id);
+ autosize.update(textarea);
+ pictrsDeleteToast(
+ i18n.t('click_to_delete_picture'),
+ i18n.t('picture_deleted'),
+ deleteUrl
+ );
+ } else {
+ i.state.imageLoading = false;
+ i.setState(i.state);
+ toast(JSON.stringify(res), 'danger');
+ }
})
.catch(error => {
i.state.imageLoading = false;
@@ -343,14 +373,10 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
if (UserService.Instance.user) {
if (res.op == UserOperation.CreateComment) {
let data = res.data as CommentResponse;
- if (data.comment.creator_id == UserService.Instance.user.id) {
- this.handleFinished();
- }
+ this.handleFinished(data);
} else if (res.op == UserOperation.EditComment) {
let data = res.data as CommentResponse;
- if (data.comment.creator_id == UserService.Instance.user.id) {
- this.handleFinished();
- }
+ this.handleFinished(data);
}
}
}
diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx
index ca828a45..155efe8e 100644
--- a/ui/src/components/comment-node.tsx
+++ b/ui/src/components/comment-node.tsx
@@ -132,7 +132,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
>
<div
id={`comment-${node.comment.id}`}
- className={`details comment-node border-top border-light ${
+ className={`details comment-node border-top border-light py-2 ${
this.isCommentNew ? 'mark' : ''
}`}
style={
@@ -148,7 +148,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
'ml-2'
}`}
>
- <div class="d-flex flex-wrap align-items-center mb-1 mt-1 text-muted small">
+ <div class="d-flex flex-wrap align-items-center text-muted small">
<span class="mr-2">
<UserListing
user={{
@@ -299,25 +299,6 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
)}
<button
class="btn btn-link btn-animate text-muted"
- onClick={linkEvent(this, this.handleSaveCommentClick)}
- data-tippy-content={
- node.comment.saved ? i18n.t('unsave') : i18n.t('save')
- }
- >
- {this.state.saveLoading ? (
- this.loadingIcon
- ) : (
- <svg
- class={`icon icon-inline ${
- node.comment.saved && 'text-warning'
- }`}
- >
- <use xlinkHref="#icon-star"></use>
- </svg>
- )}
- </button>
- <button
- class="btn btn-link btn-animate text-muted"
onClick={linkEvent(this, this.handleReplyClick)}
data-tippy-content={i18n.t('reply')}
>
@@ -352,6 +333,30 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
)}
{!this.props.showContext && this.linkBtn}
<button
+ class="btn btn-link btn-animate text-muted"
+ onClick={linkEvent(
+ this,
+ this.handleSaveCommentClick
+ )}
+ data-tippy-content={
+ node.comment.saved
+ ? i18n.t('unsave')
+ : i18n.t('save')
+ }
+ >
+ {this.state.saveLoading ? (
+ this.loadingIcon
+ ) : (
+ <svg
+ class={`icon icon-inline ${
+ node.comment.saved && 'text-warning'
+ }`}
+ >
+ <use xlinkHref="#icon-star"></use>
+ </svg>
+ )}
+ </button>
+ <button
className="btn btn-link btn-animate text-muted"
onClick={linkEvent(this, this.handleViewSource)}
data-tippy-content={i18n.t('view_source')}
diff --git a/ui/src/components/community-form.tsx b/ui/src/components/community-form.tsx
index eedc2003..90e12738 100644
--- a/ui/src/components/community-form.tsx
+++ b/ui/src/components/community-form.tsx
@@ -207,7 +207,11 @@ export class CommunityForm extends Component<
)}
<div class="form-group row">
<div class="col-12">
- <button type="submit" class="btn btn-secondary mr-2">
+ <button
+ type="submit"
+ class="btn btn-secondary mr-2"
+ disabled={this.state.loading}
+ >
{this.state.loading ? (
<svg class="icon icon-spinner spin">
<use xlinkHref="#icon-spinner"></use>
diff --git a/ui/src/components/inbox.tsx b/ui/src/components/inbox.tsx
index 4fa1498a..edbacd51 100644
--- a/ui/src/components/inbox.tsx
+++ b/ui/src/components/inbox.tsx
@@ -123,7 +123,10 @@ export class Inbox extends Component<any, InboxState> {
this.state.unreadOrAll == UnreadOrAll.Unread && (
<ul class="list-inline mb-1 text-muted small font-weight-bold">
<li className="list-inline-item">
- <span class="pointer" onClick={this.markAllAsRead}>
+ <span
+ class="pointer"
+ onClick={linkEvent(this, this.markAllAsRead)}
+ >
{i18n.t('mark_all_as_read')}
</span>
</li>
@@ -392,8 +395,14 @@ export class Inbox extends Component<any, InboxState> {
this.refetch();
}
- markAllAsRead() {
+ markAllAsRead(i: Inbox) {
WebSocketService.Instance.markAllAsRead();
+ i.state.replies = [];
+ i.state.mentions = [];
+ i.state.messages = [];
+ i.sendUnreadCount();
+ window.scrollTo(0, 0);
+ i.setState(i.state);
}
parseMessage(msg: WebSocketJsonResponse) {
@@ -447,12 +456,7 @@ export class Inbox extends Component<any, InboxState> {
this.setState(this.state);
setupTippy();
} else if (res.op == UserOperation.MarkAllAsRead) {
- this.state.replies = [];
- this.state.mentions = [];
- this.state.messages = [];
- this.sendUnreadCount();
- window.scrollTo(0, 0);
- this.setState(this.state);
+ // Moved to be instant
} else if (res.op == UserOperation.EditComment) {
let data = res.data as CommentResponse;
editCommentRes(data, this.state.replies);
diff --git a/ui/src/components/login.tsx b/ui/src/components/login.tsx
index 84014f68..ce04d0d4 100644
--- a/ui/src/components/login.tsx
+++ b/ui/src/components/login.tsx
@@ -111,6 +111,7 @@ export class Login extends Component<any, State> {
required
/>
<button
+ type="button"
disabled={!validEmail(this.state.loginForm.username_or_email)}
onClick={linkEvent(this, this.handlePasswordReset)}
className="btn p-0 btn-link d-inline-block float-right text-muted small font-weight-bold"
diff --git a/ui/src/components/navbar.tsx b/ui/src/components/navbar.tsx
index f1936be1..4cb74391 100644
--- a/ui/src/components/navbar.tsx
+++ b/ui/src/components/navbar.tsx
@@ -22,7 +22,7 @@ import {
} from '../interfaces';
import {
wsJsonToRes,
- pictshareAvatarThumbnail,
+ pictrsAvatarThumbnail,
showAvatars,
fetchLimit,
isCommentType,
@@ -218,7 +218,7 @@ export class Navbar extends Component<any, NavbarState> {
<span>
{UserService.Instance.user.avatar && showAvatars() && (
<img
- src={pictshareAvatarThumbnail(
+ src={pictrsAvatarThumbnail(
UserService.Instance.user.avatar
)}
height="32"
diff --git a/ui/src/components/post-form.tsx b/ui/src/components/post-form.tsx
index 22224c34..9f5aa363 100644
--- a/ui/src/components/post-form.tsx
+++ b/ui/src/components/post-form.tsx
@@ -36,6 +36,7 @@ import {
setupTippy,
emojiPicker,
hostname,
+ pictrsDeleteToast,
} from '../utils';
import autosize from 'autosize';
import Tribute from 'tributejs/src/Tribute.js';
@@ -284,7 +285,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
/>
{this.state.previewMode && (
<div
- className="md-div"
+ className="card card-body md-div"
dangerouslySetInnerHTML={mdToHtml(this.state.postForm.body)}
/>
)}
@@ -364,7 +365,9 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
<div class="form-group row">
<div class="col-sm-10">
<button
- disabled={!this.state.postForm.community_id}
+ disabled={
+ !this.state.postForm.community_id || this.state.loading
+ }
type="submit"
class="btn btn-secondary mr-2"
>
@@ -410,6 +413,12 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
handlePostSubmit(i: PostForm, event: any) {
event.preventDefault();
+
+ // Coerce empty url string to undefined
+ if (i.state.postForm.url && i.state.postForm.url === '') {
+ i.state.postForm.url = undefined;
+ }
+
if (i.props.post) {
WebSocketService.Instance.editPost(i.state.postForm);
} else {
@@ -523,9 +532,9 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
file = event;
}
- const imageUploadUrl = `/pictshare/api/upload.php`;
+ const imageUploadUrl = `/pictrs/image`;
const formData = new FormData();
- formData.append('file', file);
+ formData.append('images[]', file);
i.state.imageLoading = true;
i.setState(i.state);
@@ -536,13 +545,26 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
})
.then(res => res.json())
.then(res => {
- let url = `${window.location.origin}/pictshare/${encodeURI(res.url)}`;
- if (res.filetype == 'mp4') {
- url += '/raw';
+ console.log('pictrs upload:');
+ console.log(res);
+ if (res.msg == 'ok') {
+ let hash = res.files[0].file;
+ let url = `${window.location.origin}/pictrs/image/${hash}`;
+ let deleteToken = res.files[0].delete_token;
+ let deleteUrl = `${window.location.origin}/pictrs/image/delete/${deleteToken}/${hash}`;
+ i.state.postForm.url = url;
+ i.state.imageLoading = false;
+ i.setState(i.state);
+ pictrsDeleteToast(
+ i18n.t('click_to_delete_picture'),
+ i18n.t('picture_deleted'),
+ deleteUrl
+ );
+ } else {
+ i.state.imageLoading = false;
+ i.setState(i.state);
+ toast(JSON.stringify(res), 'danger');
}
- i.state.postForm.url = url;
- i.state.imageLoading = false;
- i.setState(i.state);
})
.catch(error => {
i.state.imageLoading = false;
diff --git a/ui/src/components/post-listing.tsx b/ui/src/components/post-listing.tsx
index def42e3c..b4cc4f92 100644
--- a/ui/src/components/post-listing.tsx
+++ b/ui/src/components/post-listing.tsx
@@ -29,7 +29,7 @@ import {
isImage,
isVideo,
getUnixTime,
- pictshareImage,
+ pictrsImage,
setupTippy,
hostname,
previewLines,
@@ -163,15 +163,15 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
getImage(thumbnail: boolean = false) {
let post = this.props.post;
if (isImage(post.url)) {
- if (post.url.includes('pictshare')) {
- return pictshareImage(post.url, thumbnail);
+ if (post.url.includes('pictrs')) {
+ return pictrsImage(post.url, thumbnail);
} else if (post.thumbnail_url) {
- return pictshareImage(post.thumbnail_url, thumbnail);
+ return pictrsImage(post.thumbnail_url, thumbnail);
} else {
return post.url;
}
} else if (post.thumbnail_url) {
- return pictshareImage(post.thumbnail_url, thumbnail);
+ return pictrsImage(post.thumbnail_url, thumbnail);
}
}
diff --git a/ui/src/components/private-message-form.tsx b/ui/src/components/private-message-form.tsx
index 64e22d61..8cb7590e 100644
--- a/ui/src/components/private-message-form.tsx
+++ b/ui/src/components/private-message-form.tsx
@@ -157,7 +157,7 @@ export class PrivateMessageForm extends Component<
/>
{this.state.previewMode && (
<div
- className="md-div"
+ className="card card-body md-div"
dangerouslySetInnerHTML={mdToHtml(
this.state.privateMessageForm.content
)}
@@ -186,7 +186,11 @@ export class PrivateMessageForm extends Component<
)}
<div class="form-group row">
<div class="offset-sm-2 col-sm-10">
- <button type="submit" class="btn btn-secondary mr-2">
+ <button
+ type="submit"
+ class="btn btn-secondary mr-2"
+ disabled={this.state.loading}
+ >
{this.state.loading ? (
<svg class="icon icon-spinner spin">
<use xlinkHref="#icon-spinner"></use>
diff --git a/ui/src/components/private-message.tsx b/ui/src/components/private-message.tsx
index 3acd6e19..71924f0c 100644
--- a/ui/src/components/private-message.tsx
+++ b/ui/src/components/private-message.tsx
@@ -5,12 +5,7 @@ import {
EditPrivateMessageForm,
} from '../interfaces';
import { WebSocketService, UserService } from '../services';
-import {
- mdToHtml,
- pictshareAvatarThumbnail,
- showAvatars,
- toast,
-} from '../utils';
+import { mdToHtml, pictrsAvatarThumbnail, showAvatars, toast } from '../utils';
import { MomentTime } from './moment-time';
import { PrivateMessageForm } from './private-message-form';
import { i18n } from '../i18next';
@@ -78,7 +73,7 @@ export class PrivateMessage extends Component<
<img
height="32"
width="32"
- src={pictshareAvatarThumbnail(
+ src={pictrsAvatarThumbnail(
this.mine
? message.recipient_avatar
: message.creator_avatar
diff --git a/ui/src/components/site-form.tsx b/ui/src/components/site-form.tsx
index f0c80585..a51286c8 100644
--- a/ui/src/components/site-form.tsx
+++ b/ui/src/components/site-form.tsx
@@ -78,7 +78,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
<form onSubmit={linkEvent(this, this.handleCreateSiteSubmit)}>
<h5>{`${
this.props.site
- ? capitalizeFirstLetter(i18n.t('edit'))
+ ? capitalizeFirstLetter(i18n.t('save'))
: capitalizeFirstLetter(i18n.t('name'))
} ${i18n.t('your_site')}`}</h5>
<div class="form-group row">
@@ -175,7 +175,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
</div>
<div class="form-group row">
<div class="col-12">
- <button type="submit" class="btn btn-secondary mr-2">
+ <button
+ type="submit"
+ class="btn btn-secondary mr-2"
+ disabled={this.state.loading}
+ >
{this.state.loading ? (
<svg class="icon icon-spinner spin">
<use xlinkHref="#icon-spinner"></use>
diff --git a/ui/src/components/user-listing.tsx b/ui/src/components/user-listing.tsx
index 903aa1a7..0e150b94 100644
--- a/ui/src/components/user-listing.tsx
+++ b/ui/src/components/user-listing.tsx
@@ -1,7 +1,7 @@
import { Component } from 'inferno';
import { Link } from 'inferno-router';
import { UserView } from '../interfaces';
-import { pictshareAvatarThumbnail, showAvatars, hostname } from '../utils';
+import { pictrsAvatarThumbnail, showAvatars, hostname } from '../utils';
interface UserOther {
name: string;
@@ -40,7 +40,7 @@ export class UserListing extends Component<UserListingProps, any> {
<img
height="32"
width="32"
- src={pictshareAvatarThumbnail(user.avatar)}
+ src={pictrsAvatarThumbnail(user.avatar)}
class="rounded-circle mr-2"
/>
)}
diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx
index 78807153..f635a1cd 100644
--- a/ui/src/components/user.tsx
+++ b/ui/src/components/user.tsx
@@ -927,7 +927,7 @@ export class User extends Component<any, UserState> {
handleUserSettingsThemeChange(i: User, event: any) {
i.state.userSettingsForm.theme = event.target.value;
- setTheme(event.target.value);
+ setTheme(event.target.value, true);
i.setState(i.state);
}
@@ -993,9 +993,9 @@ export class User extends Component<any, UserState> {
handleImageUpload(i: User, event: any) {
event.preventDefault();
let file = event.target.files[0];
- const imageUploadUrl = `/pictshare/api/upload.php`;
+ const imageUploadUrl = `/pictrs/image`;
const formData = new FormData();
- formData.append('file', file);
+ formData.append('images[]', file);
i.state.avatarLoading = true;
i.setState(i.state);
@@ -1006,14 +1006,19 @@ export class User extends Component<any, UserState> {
})
.then(res => res.json())
.then(res => {
- let url = `${window.location.origin}/pictshare/${res.url}`;
- if (res.filetype == 'mp4') {
- url += '/raw';
+ console.log('pictrs upload:');
+ console.log(res);
+ if (res.msg == 'ok') {
+ let hash = res.files[0].file;
+ let url = `${window.location.origin}/pictrs/image/${hash}`;
+ i.state.userSettingsForm.avatar = url;
+ i.state.avatarLoading = false;
+ i.setState(i.state);
+ } else {
+ i.state.avatarLoading = false;
+ i.setState(i.state);
+ toast(JSON.stringify(res), 'danger');
}
- i.state.userSettingsForm.avatar = url;
- console.log(url);
- i.state.avatarLoading = false;
- i.setState(i.state);
})
.catch(error => {
i.state.avatarLoading = false;