Adding a toaster to replace alerts. Fixes #457

This commit is contained in:
Dessalines 2020-01-22 22:29:11 -05:00
parent 5197407dd2
commit 9ccff18f23
27 changed files with 162 additions and 90 deletions

18
README.md vendored
View file

@ -157,15 +157,15 @@ If you'd like to add translations, take a look a look at the [English translatio
lang | done | missing lang | done | missing
--- | --- | --- --- | --- | ---
de | 88% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,docs,message_sent,messages,old_password,matrix_user_id,private_message_disclaimer,send_notifications_to_email,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message de | 88% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,docs,message_sent,messages,old_password,matrix_user_id,private_message_disclaimer,send_notifications_to_email,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
eo | 76% | number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,donate_to_lemmy,donate,from,are_you_sure,yes,no,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message eo | 76% | number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,theme,donate_to_lemmy,donate,from,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
es | 84% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message es | 83% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
fr | 84% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message fr | 83% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
it | 85% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message it | 84% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
nl | 93% | create_private_message,send_secure_message,send_message,message,message_sent,messages,matrix_user_id,private_message_disclaimer,donate_to_lemmy,donate,from,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message nl | 93% | create_private_message,send_secure_message,send_message,message,message_sent,messages,matrix_user_id,private_message_disclaimer,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
ru | 72% | cross_posts,cross_post,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message ru | 72% | cross_posts,cross_post,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
sv | 84% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message sv | 83% | create_private_message,send_secure_message,send_message,message,avatar,upload_avatar,show_avatars,archive_link,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,donate_to_lemmy,donate,from,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
zh | 71% | cross_posts,cross_post,users,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message zh | 70% | cross_posts,cross_post,users,number_of_communities,create_private_message,send_secure_message,send_message,message,preview,upload_image,avatar,upload_avatar,show_avatars,formatting_help,view_source,sticky,unsticky,archive_link,settings,stickied,delete_account,delete_account_confirm,banned,creator,number_online,docs,replies,mentions,message_sent,messages,old_password,forgot_password,reset_password_mail_sent,password_change,new_password,no_email_setup,matrix_user_id,private_message_disclaimer,send_notifications_to_email,language,browser_default,downvotes_disabled,enable_downvotes,open_registration,registration_closed,enable_nsfw,recent_comments,nsfw,show_nsfw,theme,donate_to_lemmy,donate,monero,by,to,from,transfer_community,transfer_site,are_you_sure,yes,no,logged_in,email_already_exists,couldnt_create_private_message,no_private_message_edit_allowed,couldnt_update_private_message
<!-- translationsstop --> <!-- translationsstop -->

78
ui/assets/css/toastify.css vendored Normal file
View file

@ -0,0 +1,78 @@
/*!
* Toastify js 1.6.2
* https://github.com/apvarun/toastify-js
* @license MIT licensed
*
* Copyright (C) 2018 Varun A P
*/
.toastify {
padding: 12px 20px;
color: #ffffff;
display: inline-block;
box-shadow: 0 3px 6px -1px rgba(0, 0, 0, 0.12), 0 10px 36px -4px rgba(77, 96, 232, 0.3);
background: -webkit-linear-gradient(315deg, #73a5ff, #5477f5);
background: linear-gradient(135deg, #73a5ff, #5477f5);
position: fixed;
opacity: 0;
transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
border-radius: 2px;
cursor: pointer;
text-decoration: none;
max-width: calc(50% - 20px);
z-index: 2147483647;
}
.toastify.on {
opacity: 1;
}
.toast-close {
opacity: 0.4;
padding: 0 5px;
}
.toastify-right {
right: 15px;
}
.toastify-left {
left: 15px;
}
.toastify-top {
top: -150px;
}
.toastify-bottom {
bottom: -150px;
}
.toastify-rounded {
border-radius: 25px;
}
.toastify-avatar {
width: 1.5em;
height: 1.5em;
margin: 0 5px;
border-radius: 2px;
}
.toastify-center {
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
max-width: fit-content;
}
@media only screen and (max-width: 360px) {
.toastify-right, .toastify-left {
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
max-width: fit-content;
}
}

1
ui/package.json vendored
View file

@ -36,6 +36,7 @@
"prettier": "^1.18.2", "prettier": "^1.18.2",
"rxjs": "^6.4.0", "rxjs": "^6.4.0",
"terser": "^4.6.0", "terser": "^4.6.0",
"toastify-js": "^1.6.2",
"tributejs": "^4.1.1", "tributejs": "^4.1.1",
"twemoji": "^12.1.2", "twemoji": "^12.1.2",
"ws": "^7.0.0" "ws": "^7.0.0"

View file

@ -16,6 +16,7 @@ import {
mdToHtml, mdToHtml,
randomStr, randomStr,
markdownHelpUrl, markdownHelpUrl,
toast,
} from '../utils'; } from '../utils';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import autosize from 'autosize'; import autosize from 'autosize';
@ -293,7 +294,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
.catch(error => { .catch(error => {
i.state.imageLoading = false; i.state.imageLoading = false;
i.setState(i.state); i.setState(i.state);
alert(error); toast(error, 'danger');
}); });
} }

View file

@ -12,7 +12,7 @@ import {
SortType, SortType,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService } from '../services'; import { WebSocketService } from '../services';
import { msgOp } from '../utils'; import { msgOp, toast } from '../utils';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { T } from 'inferno-i18next'; import { T } from 'inferno-i18next';
@ -235,7 +235,7 @@ export class Communities extends Component<any, CommunitiesState> {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let op: UserOperation = msgOp(msg);
if (msg.error) { if (msg.error) {
alert(i18n.t(msg.error)); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (op == UserOperation.ListCommunities) { } else if (op == UserOperation.ListCommunities) {
let res: ListCommunitiesResponse = msg; let res: ListCommunitiesResponse = msg;

View file

@ -10,8 +10,8 @@ import {
GetSiteResponse, GetSiteResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService } from '../services'; import { WebSocketService } from '../services';
import { msgOp, capitalizeFirstLetter } from '../utils'; import { msgOp, capitalizeFirstLetter, toast } from '../utils';
import * as autosize from 'autosize'; import autosize from 'autosize';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { T } from 'inferno-i18next'; import { T } from 'inferno-i18next';
@ -67,14 +67,7 @@ export class CommunityForm extends Component<
} }
this.subscription = WebSocketService.Instance.subject this.subscription = WebSocketService.Instance.subject
.pipe( .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
retryWhen(errors =>
errors.pipe(
delay(3000),
take(10)
)
)
)
.subscribe( .subscribe(
msg => this.parseMessage(msg), msg => this.parseMessage(msg),
err => console.error(err), err => console.error(err),
@ -250,7 +243,7 @@ export class CommunityForm extends Component<
let op: UserOperation = msgOp(msg); let op: UserOperation = msgOp(msg);
console.log(msg); console.log(msg);
if (msg.error) { if (msg.error) {
alert(i18n.t(msg.error)); toast(i18n.t(msg.error), 'danger');
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);
return; return;

View file

@ -24,6 +24,7 @@ import {
routeSortTypeToEnum, routeSortTypeToEnum,
fetchLimit, fetchLimit,
postRefetchSeconds, postRefetchSeconds,
toast,
} from '../utils'; } from '../utils';
import { T } from 'inferno-i18next'; import { T } from 'inferno-i18next';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
@ -257,7 +258,7 @@ export class Community extends Component<any, State> {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let op: UserOperation = msgOp(msg);
if (msg.error) { if (msg.error) {
alert(i18n.t(msg.error)); toast(i18n.t(msg.error), 'danger');
this.context.router.history.push('/'); this.context.router.history.push('/');
return; return;
} else if (op == UserOperation.GetCommunity) { } else if (op == UserOperation.GetCommunity) {

View file

@ -2,6 +2,7 @@ import { Component } from 'inferno';
import { PrivateMessageForm } from './private-message-form'; import { PrivateMessageForm } from './private-message-form';
import { WebSocketService } from '../services'; import { WebSocketService } from '../services';
import { PrivateMessageFormParams } from '../interfaces'; import { PrivateMessageFormParams } from '../interfaces';
import { toast } from '../utils';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
export class CreatePrivateMessage extends Component<any, any> { export class CreatePrivateMessage extends Component<any, any> {
@ -44,7 +45,7 @@ export class CreatePrivateMessage extends Component<any, any> {
} }
handlePrivateMessageCreate() { handlePrivateMessageCreate() {
alert(i18n.t('message_sent')); toast(i18n.t('message_sent'));
// Navigate to the front // Navigate to the front
this.props.history.push(`/`); this.props.history.push(`/`);

View file

@ -18,7 +18,7 @@ import {
PrivateMessageResponse, PrivateMessageResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { msgOp, fetchLimit, isCommentType } from '../utils'; import { msgOp, fetchLimit, isCommentType, toast } from '../utils';
import { CommentNodes } from './comment-nodes'; import { CommentNodes } from './comment-nodes';
import { PrivateMessage } from './private-message'; import { PrivateMessage } from './private-message';
import { SortSelect } from './sort-select'; import { SortSelect } from './sort-select';
@ -198,11 +198,7 @@ export class Inbox extends Component<any, InboxState> {
<div> <div>
{combined.map(i => {combined.map(i =>
isCommentType(i) ? ( isCommentType(i) ? (
<CommentNodes <CommentNodes nodes={[{ comment: i }]} noIndent markable />
nodes={[{ comment: i }]}
noIndent
markable
/>
) : ( ) : (
<PrivateMessage privateMessage={i} /> <PrivateMessage privateMessage={i} />
) )
@ -328,7 +324,7 @@ export class Inbox extends Component<any, InboxState> {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let op: UserOperation = msgOp(msg);
if (msg.error) { if (msg.error) {
alert(i18n.t(msg.error)); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (op == UserOperation.GetReplies) { } else if (op == UserOperation.GetReplies) {
let res: GetRepliesResponse = msg; let res: GetRepliesResponse = msg;
@ -423,7 +419,7 @@ export class Inbox extends Component<any, InboxState> {
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreateComment) { } else if (op == UserOperation.CreateComment) {
// let res: CommentResponse = msg; // let res: CommentResponse = msg;
alert(i18n.t('reply_sent')); toast(i18n.t('reply_sent'));
// this.state.replies.unshift(res.comment); // TODO do this right // this.state.replies.unshift(res.comment); // TODO do this right
// this.setState(this.state); // this.setState(this.state);
} else if (op == UserOperation.SaveComment) { } else if (op == UserOperation.SaveComment) {

View file

@ -10,7 +10,7 @@ import {
GetSiteResponse, GetSiteResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { msgOp, validEmail } from '../utils'; import { msgOp, validEmail, toast } from '../utils';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { T } from 'inferno-i18next'; import { T } from 'inferno-i18next';
@ -48,14 +48,7 @@ export class Login extends Component<any, State> {
this.state = this.emptyState; this.state = this.emptyState;
this.subscription = WebSocketService.Instance.subject this.subscription = WebSocketService.Instance.subject
.pipe( .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
retryWhen(errors =>
errors.pipe(
delay(3000),
take(10)
)
)
)
.subscribe( .subscribe(
msg => this.parseMessage(msg), msg => this.parseMessage(msg),
err => console.error(err), err => console.error(err),
@ -302,7 +295,7 @@ export class Login extends Component<any, State> {
parseMessage(msg: any) { parseMessage(msg: any) {
let op: UserOperation = msgOp(msg); let op: UserOperation = msgOp(msg);
if (msg.error) { if (msg.error) {
alert(i18n.t(msg.error)); toast(i18n.t(msg.error), 'danger');
this.state = this.emptyState; this.state = this.emptyState;
this.setState(this.state); this.setState(this.state);
return; return;
@ -312,6 +305,7 @@ export class Login extends Component<any, State> {
this.setState(this.state); this.setState(this.state);
let res: LoginResponse = msg; let res: LoginResponse = msg;
UserService.Instance.login(res); UserService.Instance.login(res);
toast(i18n.t('logged_in'));
this.props.history.push('/'); this.props.history.push('/');
} else if (op == UserOperation.Register) { } else if (op == UserOperation.Register) {
this.state = this.emptyState; this.state = this.emptyState;
@ -320,7 +314,7 @@ export class Login extends Component<any, State> {
UserService.Instance.login(res); UserService.Instance.login(res);
this.props.history.push('/communities'); this.props.history.push('/communities');
} else if (op == UserOperation.PasswordReset) { } else if (op == UserOperation.PasswordReset) {
alert(i18n.t('reset_password_mail_sent')); toast(i18n.t('reset_password_mail_sent'));
} else if (op == UserOperation.GetSite) { } else if (op == UserOperation.GetSite) {
let res: GetSiteResponse = msg; let res: GetSiteResponse = msg;
this.state.enable_nsfw = res.site.enable_nsfw; this.state.enable_nsfw = res.site.enable_nsfw;

View file

@ -33,6 +33,7 @@ import {
postRefetchSeconds, postRefetchSeconds,
pictshareAvatarThumbnail, pictshareAvatarThumbnail,
showAvatars, showAvatars,
toast,
} from '../utils'; } from '../utils';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { T } from 'inferno-i18next'; import { T } from 'inferno-i18next';
@ -566,7 +567,7 @@ export class Main extends Component<any, MainState> {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let op: UserOperation = msgOp(msg);
if (msg.error) { if (msg.error) {
alert(i18n.t(msg.error)); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (op == UserOperation.GetFollowedCommunities) { } else if (op == UserOperation.GetFollowedCommunities) {
let res: GetFollowedCommunitiesResponse = msg; let res: GetFollowedCommunitiesResponse = msg;

View file

@ -17,9 +17,9 @@ import {
ModAdd, ModAdd,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService } from '../services'; import { WebSocketService } from '../services';
import { msgOp, addTypeInfo, fetchLimit } from '../utils'; import { msgOp, addTypeInfo, fetchLimit, toast } from '../utils';
import { MomentTime } from './moment-time'; import { MomentTime } from './moment-time';
import * as moment from 'moment'; import moment from 'moment';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
interface ModlogState { interface ModlogState {
@ -55,14 +55,7 @@ export class Modlog extends Component<any, ModlogState> {
? Number(this.props.match.params.community_id) ? Number(this.props.match.params.community_id)
: undefined; : undefined;
this.subscription = WebSocketService.Instance.subject this.subscription = WebSocketService.Instance.subject
.pipe( .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
retryWhen(errors =>
errors.pipe(
delay(3000),
take(10)
)
)
)
.subscribe( .subscribe(
msg => this.parseMessage(msg), msg => this.parseMessage(msg),
err => console.error(err), err => console.error(err),
@ -433,7 +426,7 @@ export class Modlog extends Component<any, ModlogState> {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let op: UserOperation = msgOp(msg);
if (msg.error) { if (msg.error) {
alert(i18n.t(msg.error)); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (op == UserOperation.GetModlog) { } else if (op == UserOperation.GetModlog) {
let res: GetModlogResponse = msg; let res: GetModlogResponse = msg;

View file

@ -1,5 +1,5 @@
import { Component } from 'inferno'; import { Component } from 'inferno';
import * as moment from 'moment'; import moment from 'moment';
import { getMomentLanguage } from '../utils'; import { getMomentLanguage } from '../utils';
import { i18n } from '../i18next'; import { i18n } from '../i18next';

View file

@ -22,6 +22,7 @@ import {
showAvatars, showAvatars,
fetchLimit, fetchLimit,
isCommentType, isCommentType,
toast,
} from '../utils'; } from '../utils';
import { version } from '../version'; import { version } from '../version';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
@ -318,7 +319,7 @@ export class Navbar extends Component<any, NavbarState> {
if (UserService.Instance.user) { if (UserService.Instance.user) {
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
if (!Notification) { if (!Notification) {
alert(i18n.t('notifications_error')); toast(i18n.t('notifications_error'), 'danger');
return; return;
} }

View file

@ -7,7 +7,7 @@ import {
PasswordChangeForm, PasswordChangeForm,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { msgOp, capitalizeFirstLetter } from '../utils'; import { msgOp, capitalizeFirstLetter, toast } from '../utils';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { T } from 'inferno-i18next'; import { T } from 'inferno-i18next';
@ -34,14 +34,7 @@ export class PasswordChange extends Component<any, State> {
this.state = this.emptyState; this.state = this.emptyState;
this.subscription = WebSocketService.Instance.subject this.subscription = WebSocketService.Instance.subject
.pipe( .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
retryWhen(errors =>
errors.pipe(
delay(3000),
take(10)
)
)
)
.subscribe( .subscribe(
msg => this.parseMessage(msg), msg => this.parseMessage(msg),
err => console.error(err), err => console.error(err),
@ -143,7 +136,7 @@ export class PasswordChange extends Component<any, State> {
parseMessage(msg: any) { parseMessage(msg: any) {
let op: UserOperation = msgOp(msg); let op: UserOperation = msgOp(msg);
if (msg.error) { if (msg.error) {
alert(i18n.t(msg.error)); toast(i18n.t(msg.error), 'danger');
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);
return; return;

View file

@ -28,6 +28,7 @@ import {
mdToHtml, mdToHtml,
debounce, debounce,
isImage, isImage,
toast,
} from '../utils'; } from '../utils';
import autosize from 'autosize'; import autosize from 'autosize';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
@ -453,14 +454,14 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
.catch(error => { .catch(error => {
i.state.imageLoading = false; i.state.imageLoading = false;
i.setState(i.state); i.setState(i.state);
alert(error); toast(error, 'danger');
}); });
} }
parseMessage(msg: any) { parseMessage(msg: any) {
let op: UserOperation = msgOp(msg); let op: UserOperation = msgOp(msg);
if (msg.error) { if (msg.error) {
alert(i18n.t(msg.error)); toast(i18n.t(msg.error), 'danger');
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);
return; return;

View file

@ -28,7 +28,7 @@ import {
GetCommunityResponse, GetCommunityResponse,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { msgOp, hotRank } from '../utils'; import { msgOp, hotRank, toast } from '../utils';
import { PostListing } from './post-listing'; import { PostListing } from './post-listing';
import { PostListings } from './post-listings'; import { PostListings } from './post-listings';
import { Sidebar } from './sidebar'; import { Sidebar } from './sidebar';
@ -345,7 +345,7 @@ export class Post extends Component<any, PostState> {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let op: UserOperation = msgOp(msg);
if (msg.error) { if (msg.error) {
alert(i18n.t(msg.error)); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (op == UserOperation.GetPost) { } else if (op == UserOperation.GetPost) {
let res: GetPostResponse = msg; let res: GetPostResponse = msg;

View file

@ -22,6 +22,7 @@ import {
mdToHtml, mdToHtml,
showAvatars, showAvatars,
pictshareAvatarThumbnail, pictshareAvatarThumbnail,
toast,
} from '../utils'; } from '../utils';
import autosize from 'autosize'; import autosize from 'autosize';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
@ -268,7 +269,7 @@ export class PrivateMessageForm extends Component<
parseMessage(msg: any) { parseMessage(msg: any) {
let op: UserOperation = msgOp(msg); let op: UserOperation = msgOp(msg);
if (msg.error) { if (msg.error) {
alert(i18n.t(msg.error)); toast(i18n.t(msg.error), 'danger');
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);
return; return;

View file

@ -5,7 +5,12 @@ import {
EditPrivateMessageForm, EditPrivateMessageForm,
} from '../interfaces'; } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { mdToHtml, pictshareAvatarThumbnail, showAvatars } from '../utils'; import {
mdToHtml,
pictshareAvatarThumbnail,
showAvatars,
toast,
} from '../utils';
import { MomentTime } from './moment-time'; import { MomentTime } from './moment-time';
import { PrivateMessageForm } from './private-message-form'; import { PrivateMessageForm } from './private-message-form';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
@ -244,6 +249,6 @@ export class PrivateMessage extends Component<
handlePrivateMessageCreate() { handlePrivateMessageCreate() {
this.state.showReply = false; this.state.showReply = false;
this.setState(this.state); this.setState(this.state);
alert(i18n.t('message_sent')); toast(i18n.t('message_sent'), 'danger');
} }
} }

View file

@ -23,6 +23,7 @@ import {
routeSortTypeToEnum, routeSortTypeToEnum,
pictshareAvatarThumbnail, pictshareAvatarThumbnail,
showAvatars, showAvatars,
toast,
} from '../utils'; } from '../utils';
import { PostListing } from './post-listing'; import { PostListing } from './post-listing';
import { SortSelect } from './sort-select'; import { SortSelect } from './sort-select';
@ -480,7 +481,7 @@ export class Search extends Component<any, SearchState> {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let op: UserOperation = msgOp(msg);
if (msg.error) { if (msg.error) {
alert(i18n.t(msg.error)); toast(i18n.t(msg.error), 'danger');
return; return;
} else if (op == UserOperation.Search) { } else if (op == UserOperation.Search) {
let res: SearchResponse = msg; let res: SearchResponse = msg;

View file

@ -3,7 +3,7 @@ import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { RegisterForm, LoginResponse, UserOperation } from '../interfaces'; import { RegisterForm, LoginResponse, UserOperation } from '../interfaces';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
import { msgOp } from '../utils'; import { msgOp, toast } from '../utils';
import { SiteForm } from './site-form'; import { SiteForm } from './site-form';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { T } from 'inferno-i18next'; import { T } from 'inferno-i18next';
@ -35,14 +35,7 @@ export class Setup extends Component<any, State> {
this.state = this.emptyState; this.state = this.emptyState;
this.subscription = WebSocketService.Instance.subject this.subscription = WebSocketService.Instance.subject
.pipe( .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
retryWhen(errors =>
errors.pipe(
delay(3000),
take(10)
)
)
)
.subscribe( .subscribe(
msg => this.parseMessage(msg), msg => this.parseMessage(msg),
err => console.error(err), err => console.error(err),
@ -191,7 +184,7 @@ export class Setup extends Component<any, State> {
parseMessage(msg: any) { parseMessage(msg: any) {
let op: UserOperation = msgOp(msg); let op: UserOperation = msgOp(msg);
if (msg.error) { if (msg.error) {
alert(i18n.t(msg.error)); toast(i18n.t(msg.error), 'danger');
this.state.userLoading = false; this.state.userLoading = false;
this.setState(this.state); this.setState(this.state);
return; return;

View file

@ -30,6 +30,7 @@ import {
setTheme, setTheme,
languages, languages,
showAvatars, showAvatars,
toast,
} from '../utils'; } from '../utils';
import { PostListing } from './post-listing'; import { PostListing } from './post-listing';
import { SortSelect } from './sort-select'; import { SortSelect } from './sort-select';
@ -975,7 +976,7 @@ export class User extends Component<any, UserState> {
.catch(error => { .catch(error => {
i.state.avatarLoading = false; i.state.avatarLoading = false;
i.setState(i.state); i.setState(i.state);
alert(error); toast(error, 'danger');
}); });
} }
@ -1015,7 +1016,7 @@ export class User extends Component<any, UserState> {
console.log(msg); console.log(msg);
let op: UserOperation = msgOp(msg); let op: UserOperation = msgOp(msg);
if (msg.error) { if (msg.error) {
alert(i18n.t(msg.error)); toast(i18n.t(msg.error), 'danger');
this.state.deleteAccountLoading = false; this.state.deleteAccountLoading = false;
this.state.avatarLoading = false; this.state.avatarLoading = false;
this.state.userSettingsLoading = false; this.state.userSettingsLoading = false;
@ -1069,7 +1070,7 @@ export class User extends Component<any, UserState> {
this.setState(this.state); this.setState(this.state);
} else if (op == UserOperation.CreateComment) { } else if (op == UserOperation.CreateComment) {
// let res: CommentResponse = msg; // let res: CommentResponse = msg;
alert(i18n.t('reply_sent')); toast(i18n.t('reply_sent'));
// this.state.comments.unshift(res.comment); // TODO do this right // this.state.comments.unshift(res.comment); // TODO do this right
// this.setState(this.state); // this.setState(this.state);
} else if (op == UserOperation.SaveComment) { } else if (op == UserOperation.SaveComment) {

1
ui/src/index.html vendored
View file

@ -13,6 +13,7 @@
<!-- Styles --> <!-- Styles -->
<link rel="stylesheet" type="text/css" href="/static/assets/css/tribute.css" /> <link rel="stylesheet" type="text/css" href="/static/assets/css/tribute.css" />
<link rel="stylesheet" type="text/css" href="/static/assets/css/toastify.css" />
<link rel="stylesheet" type="text/css" href="/static/assets/css/themes/darkly.min.css" id="darkly" /> <link rel="stylesheet" type="text/css" href="/static/assets/css/themes/darkly.min.css" id="darkly" />
<link rel="stylesheet" type="text/css" href="/static/assets/css/main.css" /> <link rel="stylesheet" type="text/css" href="/static/assets/css/main.css" />

View file

@ -41,6 +41,7 @@ import { Subject } from 'rxjs';
import { retryWhen, delay } from 'rxjs/operators'; import { retryWhen, delay } from 'rxjs/operators';
import { UserService } from './'; import { UserService } from './';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { toast } from '../utils';
export class WebSocketService { export class WebSocketService {
private static _instance: WebSocketService; private static _instance: WebSocketService;
@ -318,7 +319,7 @@ export class WebSocketService {
private setAuth(obj: any, throwErr: boolean = true) { private setAuth(obj: any, throwErr: boolean = true) {
obj.auth = UserService.Instance.auth; obj.auth = UserService.Instance.auth;
if (obj.auth == null && throwErr) { if (obj.auth == null && throwErr) {
alert(i18n.t('not_logged_in')); toast(i18n.t('not_logged_in'), 'danger');
throw 'Not logged in'; throw 'Not logged in';
} }
} }

View file

@ -191,6 +191,7 @@ export const en = {
landing_0: landing_0:
"Lemmy is a <1>link aggregator</1> / reddit alternative, intended to work in the <2>fediverse</2>.<3></3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB</4>). Federation into the ActivityPub network is on the roadmap. <5></5>This is a <6>very early beta version</6>, and a lot of features are currently broken or missing. <7></7>Suggest new features or report bugs <8>here.</8><9></9>Made with <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.", "Lemmy is a <1>link aggregator</1> / reddit alternative, intended to work in the <2>fediverse</2>.<3></3>It's self-hostable, has live-updating comment threads, and is tiny (<4>~80kB</4>). Federation into the ActivityPub network is on the roadmap. <5></5>This is a <6>very early beta version</6>, and a lot of features are currently broken or missing. <7></7>Suggest new features or report bugs <8>here.</8><9></9>Made with <10>Rust</10>, <11>Actix</11>, <12>Inferno</12>, <13>Typescript</13>.",
not_logged_in: 'Not logged in.', not_logged_in: 'Not logged in.',
logged_in: 'Logged in.',
community_ban: 'You have been banned from this community.', community_ban: 'You have been banned from this community.',
site_ban: 'You have been banned from the site', site_ban: 'You have been banned from the site',
couldnt_create_comment: "Couldn't create comment.", couldnt_create_comment: "Couldn't create comment.",

9
ui/src/utils.ts vendored
View file

@ -23,6 +23,7 @@ import markdownitEmoji from 'markdown-it-emoji/light';
import markdown_it_container from 'markdown-it-container'; import markdown_it_container from 'markdown-it-container';
import * as twemoji from 'twemoji'; import * as twemoji from 'twemoji';
import * as emojiShortName from 'emoji-short-name'; import * as emojiShortName from 'emoji-short-name';
import Toastify from 'toastify-js';
export const repoUrl = 'https://github.com/dessalines/lemmy'; export const repoUrl = 'https://github.com/dessalines/lemmy';
export const markdownHelpUrl = 'https://commonmark.org/help/'; export const markdownHelpUrl = 'https://commonmark.org/help/';
@ -366,3 +367,11 @@ export function imageThumbnailer(url: string): string {
export function isCommentType(item: Comment | PrivateMessage): item is Comment { export function isCommentType(item: Comment | PrivateMessage): item is Comment {
return (item as Comment).community_id !== undefined; return (item as Comment).community_id !== undefined;
} }
export function toast(text: string, background: string = 'success') {
let backgroundColor = `var(--${background})`;
Toastify({
text: text,
backgroundColor: backgroundColor,
}).showToast();
}

5
ui/yarn.lock vendored
View file

@ -4622,6 +4622,11 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2" regex-not "^1.0.2"
safe-regex "^1.1.0" safe-regex "^1.1.0"
toastify-js@^1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/toastify-js/-/toastify-js-1.6.2.tgz#38af35625797d3d3f51fa09851f0bda449271423"
integrity sha512-ECQzgjTjxaElfwp/8e8qoIYx7U5rU2G54e5aiPMv+UtmGOYEitrtNp/Kr8uMgntnQNrDZEQJNGjBtoNnEgR5EA==
toidentifier@1.0.0: toidentifier@1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"