Merge branch 'icons' into dev

This commit is contained in:
Dessalines 2020-03-06 12:48:57 -05:00
commit 10a18145fa
26 changed files with 971 additions and 515 deletions

View file

@ -74,10 +74,6 @@
border-top: 2px solid var(--dark); border-top: 2px solid var(--dark);
} }
.comment-node {
margin-bottom: 10px;
}
.vote-bar { .vote-bar {
margin-top: -6.5px; margin-top: -6.5px;
} }
@ -95,8 +91,17 @@
fill: currentColor; fill: currentColor;
vertical-align: middle; vertical-align: middle;
align-self: center; align-self: center;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
} }
.icon-inline {
margin-bottom: 2px;
}
.spin { .spin {
animation: spins 2s linear infinite; animation: spins 2s linear infinite;
@ -112,7 +117,7 @@
} }
blockquote { blockquote {
border-left: 3px solid #ccc; border-left: 2px solid var(--secondary);
margin: 0.5em 5px; margin: 0.5em 5px;
padding: 0.1em 5px; padding: 0.1em 5px;
} }
@ -225,3 +230,20 @@ hr {
height: 50px; height: 50px;
width: 50px; width: 50px;
} }
.unselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.list-inline-item-action {
display: inline-block;
}
.list-inline-item-action:not(:last-child) {
margin-right: 1.2rem;
}

1
ui/assets/css/tippy.css vendored Normal file
View file

@ -0,0 +1 @@
.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}.tippy-iOS{cursor:pointer!important;-webkit-tap-highlight-color:transparent}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{border-width:8px 8px 0;border-top-color:#333;bottom:-7px;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;border-width:0 8px 8px;border-bottom-color:#333;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:#333;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:#333;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1}

3
ui/package.json vendored
View file

@ -41,8 +41,9 @@
"reconnecting-websocket": "^4.4.0", "reconnecting-websocket": "^4.4.0",
"rxjs": "^6.4.0", "rxjs": "^6.4.0",
"terser": "^4.6.3", "terser": "^4.6.3",
"tippy.js": "^6.0.0",
"toastify-js": "^1.6.2", "toastify-js": "^1.6.2",
"tributejs": "^4.1.1", "tributejs": "^5.0.0",
"twemoji": "^12.1.2", "twemoji": "^12.1.2",
"ws": "^7.0.0" "ws": "^7.0.0"
}, },

View file

@ -141,16 +141,22 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
<a <a
href={markdownHelpUrl} href={markdownHelpUrl}
target="_blank" target="_blank"
class="d-inline-block float-right text-muted small font-weight-bold" class="d-inline-block float-right text-muted font-weight-bold"
title={i18n.t('formatting_help')}
> >
{i18n.t('formatting_help')} <svg class="icon icon-inline">
<use xlinkHref="#icon-help-circle"></use>
</svg>
</a> </a>
<form class="d-inline-block mr-2 float-right text-muted small font-weight-bold"> <form class="d-inline-block mr-3 float-right text-muted font-weight-bold">
<label <label
htmlFor={`file-upload-${this.id}`} htmlFor={`file-upload-${this.id}`}
className={`${UserService.Instance.user && 'pointer'}`} className={`${UserService.Instance.user && 'pointer'}`}
data-tippy-content={i18n.t('upload_image')}
> >
{i18n.t('upload_image')} <svg class="icon icon-inline">
<use xlinkHref="#icon-image"></use>
</svg>
</label> </label>
<input <input
id={`file-upload-${this.id}`} id={`file-upload-${this.id}`}

View file

@ -26,6 +26,8 @@ import {
isMod, isMod,
pictshareAvatarThumbnail, pictshareAvatarThumbnail,
showAvatars, showAvatars,
setupTippy,
colorList,
} from '../utils'; } from '../utils';
import moment from 'moment'; import moment from 'moment';
import { MomentTime } from './moment-time'; import { MomentTime } from './moment-time';
@ -53,6 +55,7 @@ interface CommentNodeState {
score: number; score: number;
upvotes: number; upvotes: number;
downvotes: number; downvotes: number;
borderColor: string;
} }
interface CommentNodeProps { interface CommentNodeProps {
@ -91,6 +94,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
score: this.props.node.comment.score, score: this.props.node.comment.score,
upvotes: this.props.node.comment.upvotes, upvotes: this.props.node.comment.upvotes,
downvotes: this.props.node.comment.downvotes, downvotes: this.props.node.comment.downvotes,
borderColor: this.props.node.comment.depth
? colorList[this.props.node.comment.depth % colorList.length]
: colorList[0],
}; };
constructor(props: any, context: any) { constructor(props: any, context: any) {
@ -115,294 +121,434 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
return ( return (
<div <div
className={`comment ${ className={`comment ${
node.comment.parent_id && !this.props.noIndent ? 'ml-4' : '' node.comment.parent_id && !this.props.noIndent ? 'ml-2' : ''
}`} }`}
> >
{!this.state.collapsed && ( {!node.comment.parent_id && !this.props.noIndent && (
<div <>
className={`vote-bar mr-2 float-left small text-center ${this.props <hr class="d-sm-none my-2" />
.viewOnly && 'no-click'}`} <div class="d-none d-sm-block d-sm-none my-3" />
> </>
<button
className={`vote-animate btn btn-link p-0 ${
this.state.my_vote == 1 ? 'text-info' : 'text-muted'
}`}
onClick={linkEvent(node, this.handleCommentUpvote)}
>
<svg class="icon upvote">
<use xlinkHref="#icon-arrow-up"></use>
</svg>
</button>
<div class={`font-weight-bold text-muted`}>{this.state.score}</div>
{WebSocketService.Instance.site.enable_downvotes && (
<button
className={`vote-animate btn btn-link p-0 ${
this.state.my_vote == -1 ? 'text-danger' : 'text-muted'
}`}
onClick={linkEvent(node, this.handleCommentDownvote)}
>
<svg class="icon downvote">
<use xlinkHref="#icon-arrow-down"></use>
</svg>
</button>
)}
</div>
)} )}
<div <div
id={`comment-${node.comment.id}`} id={`comment-${node.comment.id}`}
className={`details comment-node ml-4 ${ className={`details comment-node mb-1 ${
this.isCommentNew ? 'mark' : '' this.isCommentNew ? 'mark' : ''
}`} }`}
style={
!this.props.noIndent &&
this.props.node.comment.parent_id &&
`border-left: 2px ${this.state.borderColor} solid !important`
}
> >
<ul class="list-inline mb-0 text-muted small"> <div
<li className="list-inline-item"> class={`${!this.props.noIndent &&
<Link this.props.node.comment.parent_id &&
className="text-info" 'ml-2'}`}
to={`/u/${node.comment.creator_name}`} >
> <ul class="list-inline mb-1 text-muted small">
{node.comment.creator_avatar && showAvatars() && (
<img
height="32"
width="32"
src={pictshareAvatarThumbnail(node.comment.creator_avatar)}
class="rounded-circle mr-1"
/>
)}
<span>{node.comment.creator_name}</span>
</Link>
</li>
{this.isMod && (
<li className="list-inline-item badge badge-light">
{i18n.t('mod')}
</li>
)}
{this.isAdmin && (
<li className="list-inline-item badge badge-light">
{i18n.t('admin')}
</li>
)}
{this.isPostCreator && (
<li className="list-inline-item badge badge-light">
{i18n.t('creator')}
</li>
)}
{(node.comment.banned_from_community || node.comment.banned) && (
<li className="list-inline-item badge badge-danger">
{i18n.t('banned')}
</li>
)}
<li className="list-inline-item">
<span>
(<span className="text-info">+{this.state.upvotes}</span>
<span> | </span>
<span className="text-danger">-{this.state.downvotes}</span>
<span>) </span>
</span>
</li>
{this.props.showCommunity && (
<li className="list-inline-item"> <li className="list-inline-item">
<span> {i18n.t('to')} </span> <Link
<Link to={`/c/${node.comment.community_name}`}> className="text-body font-weight-bold"
{node.comment.community_name} to={`/u/${node.comment.creator_name}`}
>
{node.comment.creator_avatar && showAvatars() && (
<img
height="32"
width="32"
src={pictshareAvatarThumbnail(
node.comment.creator_avatar
)}
class="rounded-circle mr-1"
/>
)}
<span>{node.comment.creator_name}</span>
</Link> </Link>
</li> </li>
)} {this.isMod && (
<li className="list-inline-item"> <li className="list-inline-item badge badge-light">
<span> {i18n.t('mod')}
<MomentTime data={node.comment} /> </li>
</span>
</li>
<li className="list-inline-item">
<div
className="pointer text-monospace"
onClick={linkEvent(this, this.handleCommentCollapse)}
>
{this.state.collapsed ? '[+]' : '[-]'}
</div>
</li>
</ul>
{this.state.showEdit && (
<CommentForm
node={node}
edit
onReplyCancel={this.handleReplyCancel}
disabled={this.props.locked}
/>
)}
{!this.state.showEdit && !this.state.collapsed && (
<div>
{this.state.viewSource ? (
<pre>{this.commentUnlessRemoved}</pre>
) : (
<div
className="md-div"
dangerouslySetInnerHTML={mdToHtml(this.commentUnlessRemoved)}
/>
)} )}
<ul class="list-inline mb-1 text-muted small font-weight-bold"> {this.isAdmin && (
{this.props.markable && ( <li className="list-inline-item badge badge-light">
<li className="list-inline-item"> {i18n.t('admin')}
<span </li>
class="pointer" )}
onClick={linkEvent(this, this.handleMarkRead)} {this.isPostCreator && (
> <li className="list-inline-item badge badge-light">
{node.comment.read {i18n.t('creator')}
? i18n.t('mark_as_unread') </li>
: i18n.t('mark_as_read')} )}
</span> {(node.comment.banned_from_community || node.comment.banned) && (
</li> <li className="list-inline-item badge badge-danger">
)} {i18n.t('banned')}
{UserService.Instance.user && !this.props.viewOnly && ( </li>
<> )}
<li className="list-inline-item"> {this.props.showCommunity && (
<span <li className="list-inline-item">
class="pointer" <span> {i18n.t('to')} </span>
onClick={linkEvent(this, this.handleReplyClick)} <Link to={`/c/${node.comment.community_name}`}>
> {node.comment.community_name}
{i18n.t('reply')} </Link>
</span> </li>
</li> )}
<li className="list-inline-item mr-2"> <li className="list-inline-item"></li>
<span <li className="list-inline-item">
class="pointer" <span
onClick={linkEvent(this, this.handleSaveCommentClick)} className={`unselectable pointer ${this.scoreColor}`}
> onClick={linkEvent(node, this.handleCommentUpvote)}
{node.comment.saved ? i18n.t('unsave') : i18n.t('save')} data-tippy-content={i18n.t('number_of_points', {
</span> count: this.state.score,
</li> })}
{!this.myComment && ( >
<li className="list-inline-item"> <svg class="icon icon-inline mr-1">
<Link <use xlinkHref="#icon-zap"></use>
class="text-muted" </svg>
to={`/create_private_message?recipient_id=${node.comment.creator_id}`} {this.state.score}
> </span>
{i18n.t('message').toLowerCase()} </li>
</Link> <li className="list-inline-item"></li>
</li> <li className="list-inline-item">
<span>
<MomentTime data={node.comment} />
</span>
</li>
<li className="list-inline-item">
<div
className="unselectable pointer text-monospace"
onClick={linkEvent(this, this.handleCommentCollapse)}
>
{this.state.collapsed ? (
<svg class="icon icon-inline">
<use xlinkHref="#icon-plus-square"></use>
</svg>
) : (
<svg class="icon icon-inline">
<use xlinkHref="#icon-minus-square"></use>
</svg>
)}
</div>
</li>
</ul>
{this.state.showEdit && (
<CommentForm
node={node}
edit
onReplyCancel={this.handleReplyCancel}
disabled={this.props.locked}
/>
)}
{!this.state.showEdit && !this.state.collapsed && (
<div>
{this.state.viewSource ? (
<pre>{this.commentUnlessRemoved}</pre>
) : (
<div
className="md-div"
dangerouslySetInnerHTML={mdToHtml(
this.commentUnlessRemoved
)} )}
<li className="list-inline-item"> />
<Link )}
className="text-muted" <ul class="list-inline mb-0 text-muted font-weight-bold h5">
to={`/post/${node.comment.post_id}/comment/${node.comment.id}`} {this.props.markable && (
<li className="list-inline-item-action">
<span
class="pointer"
onClick={linkEvent(this, this.handleMarkRead)}
data-tippy-content={
node.comment.read
? i18n.t('mark_as_unread')
: i18n.t('mark_as_read')
}
> >
{i18n.t('link')} <svg
</Link> class={`icon icon-inline ${node.comment.read &&
</li> 'text-success'}`}
{!this.state.showAdvanced ? (
<li className="list-inline-item">
<span
className="pointer"
onClick={linkEvent(this, this.handleShowAdvanced)}
> >
{i18n.t('more')} <use xlinkHref="#icon-check"></use>
</svg>
</span>
</li>
)}
{UserService.Instance.user && !this.props.viewOnly && (
<>
<li className="list-inline-item-action">
<button
className={`vote-animate btn btn-link p-0 mb-1 ${
this.state.my_vote == 1 ? 'text-info' : 'text-muted'
}`}
onClick={linkEvent(node, this.handleCommentUpvote)}
data-tippy-content={i18n.t('upvote')}
>
<svg class="icon icon-inline">
<use xlinkHref="#icon-arrow-up"></use>
</svg>
{this.state.upvotes !== this.state.score && (
<span class="ml-1">{this.state.upvotes}</span>
)}
</button>
</li>
{WebSocketService.Instance.site.enable_downvotes && (
<li className="list-inline-item-action">
<button
className={`vote-animate btn btn-link p-0 mb-1 ${
this.state.my_vote == -1
? 'text-danger'
: 'text-muted'
}`}
onClick={linkEvent(
node,
this.handleCommentDownvote
)}
data-tippy-content={i18n.t('downvote')}
>
<svg class="icon icon-inline">
<use xlinkHref="#icon-arrow-down"></use>
</svg>
{this.state.upvotes !== this.state.score && (
<span class="ml-1">{this.state.downvotes}</span>
)}
</button>
</li>
)}
<li className="list-inline-item-action">
<span
class="pointer"
onClick={linkEvent(this, this.handleReplyClick)}
data-tippy-content={i18n.t('reply')}
>
<svg class="icon icon-inline">
<use xlinkHref="#icon-reply1"></use>
</svg>
</span> </span>
</li> </li>
) : ( <li className="list-inline-item-action">
<> <Link
<li className="list-inline-item"></li> className="text-muted"
<li className="list-inline-item"> to={`/post/${node.comment.post_id}/comment/${node.comment.id}`}
title={i18n.t('link')}
>
<svg class="icon icon-inline">
<use xlinkHref="#icon-link"></use>
</svg>
</Link>
</li>
{!this.state.showAdvanced ? (
<li className="list-inline-item-action">
<span <span
className="pointer" className="unselectable pointer"
onClick={linkEvent(this, this.handleViewSource)} onClick={linkEvent(this, this.handleShowAdvanced)}
data-tippy-content={i18n.t('more')}
> >
{i18n.t('view_source')} <svg class="icon icon-inline">
<use xlinkHref="#icon-more-vertical"></use>
</svg>
</span> </span>
</li> </li>
<li className="list-inline-item"></li> ) : (
{this.myComment && ( <>
<> {!this.myComment && (
<li className="list-inline-item"> <li className="list-inline-item-action">
<span <Link
class="pointer" class="text-muted"
onClick={linkEvent(this, this.handleEditClick)} to={`/create_private_message?recipient_id=${node.comment.creator_id}`}
title={i18n.t('message').toLowerCase()}
> >
{i18n.t('edit')} <svg class="icon">
</span> <use xlinkHref="#icon-mail"></use>
</svg>
</Link>
</li> </li>
<li className="list-inline-item"> )}
<span <li className="list-inline-item-action">
class="pointer" <span
onClick={linkEvent( class="pointer"
this, onClick={linkEvent(
this.handleDeleteClick this,
)} this.handleSaveCommentClick
>
{!node.comment.deleted
? i18n.t('delete')
: i18n.t('restore')}
</span>
</li>
</>
)}
{/* Admins and mods can remove comments */}
{(this.canMod || this.canAdmin) && (
<>
<li className="list-inline-item">
{!node.comment.removed ? (
<span
class="pointer"
onClick={linkEvent(
this,
this.handleModRemoveShow
)}
>
{i18n.t('remove')}
</span>
) : (
<span
class="pointer"
onClick={linkEvent(
this,
this.handleModRemoveSubmit
)}
>
{i18n.t('restore')}
</span>
)} )}
</li> data-tippy-content={
</> node.comment.saved
)} ? i18n.t('unsave')
{/* Mods can ban from community, and appoint as mods to community */} : i18n.t('save')
{this.canMod && ( }
<> >
{!this.isMod && ( <svg
<li className="list-inline-item"> class={`icon icon-inline ${node.comment.saved &&
{!node.comment.banned_from_community ? ( 'text-warning'}`}
>
<use xlinkHref="#icon-star"></use>
</svg>
</span>
</li>
<li className="list-inline-item-action">
<span
className="pointer"
onClick={linkEvent(this, this.handleViewSource)}
data-tippy-content={i18n.t('view_source')}
>
<svg
class={`icon icon-inline ${this.state
.viewSource && 'text-success'}`}
>
<use xlinkHref="#icon-file-text"></use>
</svg>
</span>
</li>
{this.myComment && (
<>
<li className="list-inline-item-action"></li>
<li className="list-inline-item-action">
<span
class="pointer"
onClick={linkEvent(
this,
this.handleEditClick
)}
data-tippy-content={i18n.t('edit')}
>
<svg class="icon icon-inline">
<use xlinkHref="#icon-edit"></use>
</svg>
</span>
</li>
<li className="list-inline-item-action">
<span
class="pointer"
onClick={linkEvent(
this,
this.handleDeleteClick
)}
data-tippy-content={
!node.comment.deleted
? i18n.t('delete')
: i18n.t('restore')
}
>
<svg
class={`icon icon-inline ${node.comment
.deleted && 'text-danger'}`}
>
<use xlinkHref="#icon-trash"></use>
</svg>
</span>
</li>
</>
)}
{/* Admins and mods can remove comments */}
{(this.canMod || this.canAdmin) && (
<>
<li className="list-inline-item-action">
{!node.comment.removed ? (
<span <span
class="pointer" class="pointer"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleModBanFromCommunityShow this.handleModRemoveShow
)} )}
> >
{i18n.t('ban')} {i18n.t('remove')}
</span> </span>
) : ( ) : (
<span <span
class="pointer" class="pointer"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleModBanFromCommunitySubmit this.handleModRemoveSubmit
)} )}
> >
{i18n.t('unban')} {i18n.t('restore')}
</span> </span>
)} )}
</li> </li>
)} </>
{!node.comment.banned_from_community && ( )}
<li className="list-inline-item"> {/* Mods can ban from community, and appoint as mods to community */}
{!this.state.showConfirmAppointAsMod ? ( {this.canMod && (
<>
{!this.isMod && (
<li className="list-inline-item-action">
{!node.comment.banned_from_community ? (
<span
class="pointer"
onClick={linkEvent(
this,
this.handleModBanFromCommunityShow
)}
>
{i18n.t('ban')}
</span>
) : (
<span
class="pointer"
onClick={linkEvent(
this,
this.handleModBanFromCommunitySubmit
)}
>
{i18n.t('unban')}
</span>
)}
</li>
)}
{!node.comment.banned_from_community && (
<li className="list-inline-item-action">
{!this.state.showConfirmAppointAsMod ? (
<span
class="pointer"
onClick={linkEvent(
this,
this.handleShowConfirmAppointAsMod
)}
>
{this.isMod
? i18n.t('remove_as_mod')
: i18n.t('appoint_as_mod')}
</span>
) : (
<>
<span class="d-inline-block mr-1">
{i18n.t('are_you_sure')}
</span>
<span
class="pointer d-inline-block mr-1"
onClick={linkEvent(
this,
this.handleAddModToCommunity
)}
>
{i18n.t('yes')}
</span>
<span
class="pointer d-inline-block"
onClick={linkEvent(
this,
this.handleCancelConfirmAppointAsMod
)}
>
{i18n.t('no')}
</span>
</>
)}
</li>
)}
</>
)}
{/* Community creators and admins can transfer community to another mod */}
{(this.amCommunityCreator || this.canAdmin) &&
this.isMod && (
<li className="list-inline-item-action">
{!this.state.showConfirmTransferCommunity ? (
<span <span
class="pointer" class="pointer"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleShowConfirmAppointAsMod this.handleShowConfirmTransferCommunity
)} )}
> >
{this.isMod {i18n.t('transfer_community')}
? i18n.t('remove_as_mod')
: i18n.t('appoint_as_mod')}
</span> </span>
) : ( ) : (
<> <>
@ -413,7 +559,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
class="pointer d-inline-block mr-1" class="pointer d-inline-block mr-1"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleAddModToCommunity this.handleTransferCommunity
)} )}
> >
{i18n.t('yes')} {i18n.t('yes')}
@ -422,7 +568,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
class="pointer d-inline-block" class="pointer d-inline-block"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleCancelConfirmAppointAsMod this
.handleCancelShowConfirmTransferCommunity
)} )}
> >
{i18n.t('no')} {i18n.t('no')}
@ -431,21 +578,89 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
)} )}
</li> </li>
)} )}
</> {/* Admins can ban from all, and appoint other admins */}
)} {this.canAdmin && (
{/* Community creators and admins can transfer community to another mod */} <>
{(this.amCommunityCreator || this.canAdmin) && {!this.isAdmin && (
this.isMod && ( <li className="list-inline-item-action">
<li className="list-inline-item"> {!node.comment.banned ? (
{!this.state.showConfirmTransferCommunity ? ( <span
class="pointer"
onClick={linkEvent(
this,
this.handleModBanShow
)}
>
{i18n.t('ban_from_site')}
</span>
) : (
<span
class="pointer"
onClick={linkEvent(
this,
this.handleModBanSubmit
)}
>
{i18n.t('unban_from_site')}
</span>
)}
</li>
)}
{!node.comment.banned && (
<li className="list-inline-item-action">
{!this.state.showConfirmAppointAsAdmin ? (
<span
class="pointer"
onClick={linkEvent(
this,
this.handleShowConfirmAppointAsAdmin
)}
>
{this.isAdmin
? i18n.t('remove_as_admin')
: i18n.t('appoint_as_admin')}
</span>
) : (
<>
<span class="d-inline-block mr-1">
{i18n.t('are_you_sure')}
</span>
<span
class="pointer d-inline-block mr-1"
onClick={linkEvent(
this,
this.handleAddAdmin
)}
>
{i18n.t('yes')}
</span>
<span
class="pointer d-inline-block"
onClick={linkEvent(
this,
this.handleCancelConfirmAppointAsAdmin
)}
>
{i18n.t('no')}
</span>
</>
)}
</li>
)}
</>
)}
{/* Site Creator can transfer to another admin */}
{this.amSiteCreator && this.isAdmin && (
<li className="list-inline-item-action">
{!this.state.showConfirmTransferSite ? (
<span <span
class="pointer" class="pointer"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleShowConfirmTransferCommunity this.handleShowConfirmTransferSite
)} )}
> >
{i18n.t('transfer_community')} {i18n.t('transfer_site')}
</span> </span>
) : ( ) : (
<> <>
@ -456,7 +671,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
class="pointer d-inline-block mr-1" class="pointer d-inline-block mr-1"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleTransferCommunity this.handleTransferSite
)} )}
> >
{i18n.t('yes')} {i18n.t('yes')}
@ -465,8 +680,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
class="pointer d-inline-block" class="pointer d-inline-block"
onClick={linkEvent( onClick={linkEvent(
this, this,
this this.handleCancelShowConfirmTransferSite
.handleCancelShowConfirmTransferCommunity
)} )}
> >
{i18n.t('no')} {i18n.t('no')}
@ -475,125 +689,16 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
)} )}
</li> </li>
)} )}
{/* Admins can ban from all, and appoint other admins */} </>
{this.canAdmin && ( )}
<> </>
{!this.isAdmin && ( )}
<li className="list-inline-item"> </ul>
{!node.comment.banned ? ( </div>
<span )}
class="pointer" </div>
onClick={linkEvent(
this,
this.handleModBanShow
)}
>
{i18n.t('ban_from_site')}
</span>
) : (
<span
class="pointer"
onClick={linkEvent(
this,
this.handleModBanSubmit
)}
>
{i18n.t('unban_from_site')}
</span>
)}
</li>
)}
{!node.comment.banned && (
<li className="list-inline-item">
{!this.state.showConfirmAppointAsAdmin ? (
<span
class="pointer"
onClick={linkEvent(
this,
this.handleShowConfirmAppointAsAdmin
)}
>
{this.isAdmin
? i18n.t('remove_as_admin')
: i18n.t('appoint_as_admin')}
</span>
) : (
<>
<span class="d-inline-block mr-1">
{i18n.t('are_you_sure')}
</span>
<span
class="pointer d-inline-block mr-1"
onClick={linkEvent(
this,
this.handleAddAdmin
)}
>
{i18n.t('yes')}
</span>
<span
class="pointer d-inline-block"
onClick={linkEvent(
this,
this.handleCancelConfirmAppointAsAdmin
)}
>
{i18n.t('no')}
</span>
</>
)}
</li>
)}
</>
)}
{/* Site Creator can transfer to another admin */}
{this.amSiteCreator && this.isAdmin && (
<li className="list-inline-item">
{!this.state.showConfirmTransferSite ? (
<span
class="pointer"
onClick={linkEvent(
this,
this.handleShowConfirmTransferSite
)}
>
{i18n.t('transfer_site')}
</span>
) : (
<>
<span class="d-inline-block mr-1">
{i18n.t('are_you_sure')}
</span>
<span
class="pointer d-inline-block mr-1"
onClick={linkEvent(
this,
this.handleTransferSite
)}
>
{i18n.t('yes')}
</span>
<span
class="pointer d-inline-block"
onClick={linkEvent(
this,
this.handleCancelShowConfirmTransferSite
)}
>
{i18n.t('no')}
</span>
</>
)}
</li>
)}
</>
)}
</>
)}
</ul>
</div>
)}
</div> </div>
{/* end of details */}
{this.state.showRemoveDialog && ( {this.state.showRemoveDialog && (
<form <form
class="form-inline" class="form-inline"
@ -1049,5 +1154,16 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
handleShowAdvanced(i: CommentNode) { handleShowAdvanced(i: CommentNode) {
i.state.showAdvanced = !i.state.showAdvanced; i.state.showAdvanced = !i.state.showAdvanced;
i.setState(i.state); i.setState(i.state);
setupTippy();
}
get scoreColor() {
if (this.state.my_vote == 1) {
return 'text-info';
} else if (this.state.my_vote == -1) {
return 'text-danger';
} else {
return 'text-muted';
}
} }
} }

View file

@ -43,6 +43,7 @@ import {
createPostLikeFindRes, createPostLikeFindRes,
editPostFindRes, editPostFindRes,
commentsToFlatNodes, commentsToFlatNodes,
setupTippy,
} from '../utils'; } from '../utils';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
@ -194,13 +195,14 @@ export class Community extends Component<any, State> {
selects() { selects() {
return ( return (
<div class="mb-2"> <div class="mb-3">
<DataTypeSelect <span class="mr-3">
type_={this.state.dataType} <DataTypeSelect
onChange={this.handleDataTypeChange} type_={this.state.dataType}
/> onChange={this.handleDataTypeChange}
/>
<span class="mx-3"> </span>
<span class="mr-2">
<SortSelect sort={this.state.sort} onChange={this.handleSortChange} /> <SortSelect sort={this.state.sort} onChange={this.handleSortChange} />
</span> </span>
<a <a
@ -208,6 +210,7 @@ export class Community extends Component<any, State> {
SortType[this.state.sort] SortType[this.state.sort]
}`} }`}
target="_blank" target="_blank"
title="RSS"
> >
<svg class="icon text-muted small"> <svg class="icon text-muted small">
<use xlinkHref="#icon-rss">#</use> <use xlinkHref="#icon-rss">#</use>
@ -339,6 +342,7 @@ export class Community extends Component<any, State> {
this.state.posts = data.posts; this.state.posts = data.posts;
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);
setupTippy();
} else if (res.op == UserOperation.EditPost) { } else if (res.op == UserOperation.EditPost) {
let data = res.data as PostResponse; let data = res.data as PostResponse;
editPostFindRes(data, this.state.posts); editPostFindRes(data, this.state.posts);

View file

@ -1,6 +1,7 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent } from 'inferno';
import { FramelyData } from '../interfaces'; import { FramelyData } from '../interfaces';
import { mdToHtml } from '../utils'; import { mdToHtml } from '../utils';
import { i18n } from '../i18next';
interface FramelyCardProps { interface FramelyCardProps {
iframely: FramelyData; iframely: FramelyData;
@ -54,6 +55,7 @@ export class IFramelyCard extends Component<
<span <span
class="ml-2 pointer text-monospace" class="ml-2 pointer text-monospace"
onClick={linkEvent(this, this.handleIframeExpand)} onClick={linkEvent(this, this.handleIframeExpand)}
data-tippy-content={i18n.t('expand_here')}
> >
{this.state.expanded ? '[-]' : '[+]'} {this.state.expanded ? '[-]' : '[+]'}
</span> </span>

View file

@ -116,6 +116,7 @@ export class Inbox extends Component<any, InboxState> {
<a <a
href={`/feeds/inbox/${UserService.Instance.auth}.xml`} href={`/feeds/inbox/${UserService.Instance.auth}.xml`}
target="_blank" target="_blank"
title="RSS"
> >
<svg class="icon mx-2 text-muted small"> <svg class="icon mx-2 text-muted small">
<use xlinkHref="#icon-rss">#</use> <use xlinkHref="#icon-rss">#</use>

View file

@ -52,6 +52,7 @@ import {
editPostFindRes, editPostFindRes,
commentsToFlatNodes, commentsToFlatNodes,
commentSortSortType, commentSortSortType,
setupTippy,
} from '../utils'; } from '../utils';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
import { T } from 'inferno-i18next'; import { T } from 'inferno-i18next';
@ -183,7 +184,7 @@ export class Main extends Component<any, MainState> {
<h5> <h5>
<T i18nKey="subscribed_to_communities"> <T i18nKey="subscribed_to_communities">
# #
<Link class="text-white" to="/communities"> <Link class="text-body" to="/communities">
# #
</Link> </Link>
</T> </T>
@ -221,7 +222,7 @@ export class Main extends Component<any, MainState> {
<h5> <h5>
<T i18nKey="trending_communities"> <T i18nKey="trending_communities">
# #
<Link class="text-white" to="/communities"> <Link class="text-body" to="/communities">
# #
</Link> </Link>
</T> </T>
@ -268,13 +269,16 @@ export class Main extends Component<any, MainState> {
<div class="card-body"> <div class="card-body">
<h5 class="mb-0">{`${this.state.siteRes.site.name}`}</h5> <h5 class="mb-0">{`${this.state.siteRes.site.name}`}</h5>
{this.canAdmin && ( {this.canAdmin && (
<ul class="list-inline mb-1 text-muted small font-weight-bold"> <ul class="list-inline mb-1 text-muted font-weight-bold">
<li className="list-inline-item"> <li className="list-inline-item-action">
<span <span
class="pointer" class="pointer"
onClick={linkEvent(this, this.handleEditClick)} onClick={linkEvent(this, this.handleEditClick)}
data-tippy-content={i18n.t('edit')}
> >
{i18n.t('edit')} <svg class="icon icon-inline">
<use xlinkHref="#icon-edit"></use>
</svg>
</span> </span>
</li> </li>
</ul> </ul>
@ -313,7 +317,10 @@ export class Main extends Component<any, MainState> {
<li class="list-inline-item">{i18n.t('admins')}:</li> <li class="list-inline-item">{i18n.t('admins')}:</li>
{this.state.siteRes.admins.map(admin => ( {this.state.siteRes.admins.map(admin => (
<li class="list-inline-item"> <li class="list-inline-item">
<Link class="text-info" to={`/u/${admin.name}`}> <Link
class="text-body font-weight-bold"
to={`/u/${admin.name}`}
>
{admin.avatar && showAvatars() && ( {admin.avatar && showAvatars() && (
<img <img
height="32" height="32"
@ -424,11 +431,13 @@ export class Main extends Component<any, MainState> {
selects() { selects() {
return ( return (
<div className="mb-3"> <div className="mb-3">
<DataTypeSelect <span class="mr-3">
type_={this.state.dataType} <DataTypeSelect
onChange={this.handleDataTypeChange} type_={this.state.dataType}
/> onChange={this.handleDataTypeChange}
<span class="mx-3"> />
</span>
<span class="mr-3">
<ListingTypeSelect <ListingTypeSelect
type_={this.state.listingType} type_={this.state.listingType}
onChange={this.handleListingTypeChange} onChange={this.handleListingTypeChange}
@ -441,8 +450,9 @@ export class Main extends Component<any, MainState> {
<a <a
href={`/feeds/all.xml?sort=${SortType[this.state.sort]}`} href={`/feeds/all.xml?sort=${SortType[this.state.sort]}`}
target="_blank" target="_blank"
title="RSS"
> >
<svg class="icon mx-1 text-muted small"> <svg class="icon text-muted small">
<use xlinkHref="#icon-rss">#</use> <use xlinkHref="#icon-rss">#</use>
</svg> </svg>
</a> </a>
@ -454,8 +464,9 @@ export class Main extends Component<any, MainState> {
SortType[this.state.sort] SortType[this.state.sort]
}`} }`}
target="_blank" target="_blank"
title="RSS"
> >
<svg class="icon mx-1 text-muted small"> <svg class="icon text-muted small">
<use xlinkHref="#icon-rss">#</use> <use xlinkHref="#icon-rss">#</use>
</svg> </svg>
</a> </a>
@ -613,6 +624,7 @@ export class Main extends Component<any, MainState> {
this.state.posts = data.posts; this.state.posts = data.posts;
this.state.loading = false; this.state.loading = false;
this.setState(this.state); this.setState(this.state);
setupTippy();
} else if (res.op == UserOperation.CreatePost) { } else if (res.op == UserOperation.CreatePost) {
let data = res.data as PostResponse; let data = res.data as PostResponse;

View file

@ -354,7 +354,7 @@ export class Modlog extends Component<any, ModlogState> {
<h5> <h5>
{this.state.communityName && ( {this.state.communityName && (
<Link <Link
className="text-white" className="text-body"
to={`/c/${this.state.communityName}`} to={`/c/${this.state.communityName}`}
> >
/c/{this.state.communityName}{' '} /c/{this.state.communityName}{' '}

View file

@ -1,6 +1,6 @@
import { Component } from 'inferno'; import { Component } from 'inferno';
import moment from 'moment'; import moment from 'moment';
import { getMomentLanguage } from '../utils'; import { getMomentLanguage, capitalizeFirstLetter } from '../utils';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
interface MomentTimeProps { interface MomentTimeProps {
@ -23,13 +23,35 @@ export class MomentTime extends Component<MomentTimeProps, any> {
render() { render() {
if (this.props.data.updated) { if (this.props.data.updated) {
return ( return (
<span title={this.props.data.updated} className="font-italics"> <span
{i18n.t('modified')} {moment.utc(this.props.data.updated).fromNow()} data-tippy-content={`${capitalizeFirstLetter(
i18n.t('modified')
)} ${this.format(this.props.data.updated)}`}
className="font-italics pointer unselectable"
>
<svg class="icon icon-inline mr-1">
<use xlinkHref="#icon-edit-2"></use>
</svg>
{moment.utc(this.props.data.updated).fromNow(true)}
</span> </span>
); );
} else { } else {
let str = this.props.data.published || this.props.data.when_; let str = this.props.data.published || this.props.data.when_;
return <span title={str}>{moment.utc(str).fromNow()}</span>; return (
<span
className="pointer unselectable"
data-tippy-content={this.format(str)}
>
{moment.utc(str).fromNow(true)}
</span>
);
} }
} }
format(input: string): string {
return moment
.utc(input)
.local()
.format('LLLL');
}
} }

View file

@ -105,6 +105,7 @@ export class Navbar extends Component<any, NavbarState> {
type="button" type="button"
aria-label="menu" aria-label="menu"
onClick={linkEvent(this, this.expandNavbar)} onClick={linkEvent(this, this.expandNavbar)}
data-tippy-content={i18n.t('expand_here')}
> >
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
@ -113,12 +114,16 @@ export class Navbar extends Component<any, NavbarState> {
> >
<ul class="navbar-nav mr-auto"> <ul class="navbar-nav mr-auto">
<li class="nav-item"> <li class="nav-item">
<Link class="nav-link" to="/communities"> <Link
class="nav-link"
to="/communities"
title={i18n.t('communities')}
>
{i18n.t('communities')} {i18n.t('communities')}
</Link> </Link>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<Link class="nav-link" to="/search"> <Link class="nav-link" to="/search" title={i18n.t('search')}>
{i18n.t('search')} {i18n.t('search')}
</Link> </Link>
</li> </li>
@ -129,12 +134,17 @@ export class Navbar extends Component<any, NavbarState> {
pathname: '/create_post', pathname: '/create_post',
state: { prevPath: this.currentLocation }, state: { prevPath: this.currentLocation },
}} }}
title={i18n.t('create_post')}
> >
{i18n.t('create_post')} {i18n.t('create_post')}
</Link> </Link>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<Link class="nav-link" to="/create_community"> <Link
class="nav-link"
to="/create_community"
title={i18n.t('create_community')}
>
{i18n.t('create_community')} {i18n.t('create_community')}
</Link> </Link>
</li> </li>
@ -154,9 +164,9 @@ export class Navbar extends Component<any, NavbarState> {
{this.state.isLoggedIn ? ( {this.state.isLoggedIn ? (
<> <>
<li className="nav-item mt-1"> <li className="nav-item mt-1">
<Link class="nav-link" to="/inbox"> <Link class="nav-link" to="/inbox" title={i18n.t('inbox')}>
<svg class="icon"> <svg class="icon">
<use xlinkHref="#icon-mail"></use> <use xlinkHref="#icon-bell"></use>
</svg> </svg>
{this.state.unreadCount > 0 && ( {this.state.unreadCount > 0 && (
<span class="ml-1 badge badge-light"> <span class="ml-1 badge badge-light">
@ -169,6 +179,7 @@ export class Navbar extends Component<any, NavbarState> {
<Link <Link
class="nav-link" class="nav-link"
to={`/u/${UserService.Instance.user.username}`} to={`/u/${UserService.Instance.user.username}`}
title={i18n.t('settings')}
> >
<span> <span>
{UserService.Instance.user.avatar && showAvatars() && ( {UserService.Instance.user.avatar && showAvatars() && (
@ -187,7 +198,11 @@ export class Navbar extends Component<any, NavbarState> {
</li> </li>
</> </>
) : ( ) : (
<Link class="nav-link" to="/login"> <Link
class="nav-link"
to="/login"
title={i18n.t('login_sign_up')}
>
{i18n.t('login_sign_up')} {i18n.t('login_sign_up')}
</Link> </Link>
)} )}
@ -263,7 +278,7 @@ export class Navbar extends Component<any, NavbarState> {
} else if (res.op == UserOperation.GetSite) { } else if (res.op == UserOperation.GetSite) {
let data = res.data as GetSiteResponse; let data = res.data as GetSiteResponse;
if (data.site) { if (data.site && !this.state.siteName) {
this.state.siteName = data.site.name; this.state.siteName = data.site.name;
WebSocketService.Instance.site = data.site; WebSocketService.Instance.site = data.site;
this.setState(this.state); this.setState(this.state);

View file

@ -32,6 +32,7 @@ import {
toast, toast,
randomStr, randomStr,
setupTribute, setupTribute,
setupTippy,
} from '../utils'; } from '../utils';
import autosize from 'autosize'; import autosize from 'autosize';
import Tribute from 'tributejs/src/Tribute.js'; import Tribute from 'tributejs/src/Tribute.js';
@ -142,6 +143,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
this.setState(this.state); this.setState(this.state);
autosize.update(textarea); autosize.update(textarea);
}); });
setupTippy();
} }
componentWillUnmount() { componentWillUnmount() {
@ -179,9 +181,12 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
<label <label
htmlFor="file-upload" htmlFor="file-upload"
className={`${UserService.Instance.user && className={`${UserService.Instance.user &&
'pointer'} d-inline-block mr-2 float-right text-muted small font-weight-bold`} 'pointer'} d-inline-block float-right text-muted h6 font-weight-bold`}
data-tippy-content={i18n.t('upload_image')}
> >
{i18n.t('upload_image')} <svg class="icon icon-inline">
<use xlinkHref="#icon-image"></use>
</svg>
</label> </label>
<input <input
id="file-upload" id="file-upload"
@ -279,9 +284,12 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
<a <a
href={markdownHelpUrl} href={markdownHelpUrl}
target="_blank" target="_blank"
class="d-inline-block float-right text-muted small font-weight-bold" class="d-inline-block float-right text-muted h6 font-weight-bold"
title={i18n.t('formatting_help')}
> >
{i18n.t('formatting_help')} <svg class="icon icon-inline">
<use xlinkHref="#icon-help-circle"></use>
</svg>
</a> </a>
</div> </div>
</div> </div>

View file

@ -30,6 +30,7 @@ import {
pictshareAvatarThumbnail, pictshareAvatarThumbnail,
showAvatars, showAvatars,
imageThumbnailer, imageThumbnailer,
setupTippy,
} from '../utils'; } from '../utils';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
@ -185,7 +186,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
return ( return (
<span <span
class="text-body pointer" class="text-body pointer"
title={i18n.t('expand_here')} data-tippy-content={i18n.t('expand_here')}
onClick={linkEvent(this, this.handleImageExpandClick)} onClick={linkEvent(this, this.handleImageExpandClick)}
> >
{this.imgThumb()} {this.imgThumb()}
@ -246,12 +247,18 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
this.state.my_vote == 1 ? 'text-info' : 'text-muted' this.state.my_vote == 1 ? 'text-info' : 'text-muted'
}`} }`}
onClick={linkEvent(this, this.handlePostLike)} onClick={linkEvent(this, this.handlePostLike)}
data-tippy-content={i18n.t('upvote')}
> >
<svg class="icon upvote"> <svg class="icon upvote">
<use xlinkHref="#icon-arrow-up"></use> <use xlinkHref="#icon-arrow-up"></use>
</svg> </svg>
</button> </button>
<div class={`font-weight-bold text-muted px-1`}> <div
class={`unselectable pointer font-weight-bold text-muted px-1`}
data-tippy-content={i18n.t('number_of_points', {
count: this.state.score,
})}
>
{this.state.score} {this.state.score}
</div> </div>
{WebSocketService.Instance.site.enable_downvotes && ( {WebSocketService.Instance.site.enable_downvotes && (
@ -260,6 +267,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
this.state.my_vote == -1 ? 'text-danger' : 'text-muted' this.state.my_vote == -1 ? 'text-danger' : 'text-muted'
}`} }`}
onClick={linkEvent(this, this.handlePostDisLike)} onClick={linkEvent(this, this.handlePostDisLike)}
data-tippy-content={i18n.t('downvote')}
> >
<svg class="icon downvote"> <svg class="icon downvote">
<use xlinkHref="#icon-arrow-down"></use> <use xlinkHref="#icon-arrow-down"></use>
@ -323,7 +331,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
title={this.state.url} title={this.state.url}
> >
{new URL(this.state.url).hostname} {new URL(this.state.url).hostname}
<svg class="ml-1 icon"> <svg class="ml-1 icon icon-inline">
<use xlinkHref="#icon-external-link"></use> <use xlinkHref="#icon-external-link"></use>
</svg> </svg>
</a> </a>
@ -333,19 +341,23 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<> <>
{!this.state.imageExpanded ? ( {!this.state.imageExpanded ? (
<span <span
class="text-monospace pointer ml-2 text-muted small" class="text-monospace unselectable pointer ml-2 text-muted small"
title={i18n.t('expand_here')} data-tippy-content={i18n.t('expand_here')}
onClick={linkEvent(this, this.handleImageExpandClick)} onClick={linkEvent(this, this.handleImageExpandClick)}
> >
[+] <svg class="icon icon-inline">
<use xlinkHref="#icon-plus-square"></use>
</svg>
</span> </span>
) : ( ) : (
<span> <span>
<span <span
class="text-monospace pointer ml-2 text-muted small" class="text-monospace unselectable pointer ml-2 text-muted small"
onClick={linkEvent(this, this.handleImageExpandClick)} onClick={linkEvent(this, this.handleImageExpandClick)}
> >
[-] <svg class="icon icon-inline">
<use xlinkHref="#icon-minus-square"></use>
</svg>
</span> </span>
<div> <div>
<span <span
@ -371,18 +383,33 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
</small> </small>
)} )}
{post.deleted && ( {post.deleted && (
<small className="ml-2 text-muted font-italic"> <small
{i18n.t('deleted')} className="unselectable pointer ml-2 text-muted font-italic"
data-tippy-content={i18n.t('deleted')}
>
<svg class={`icon icon-inline text-danger`}>
<use xlinkHref="#icon-trash"></use>
</svg>
</small> </small>
)} )}
{post.locked && ( {post.locked && (
<small className="ml-2 text-muted font-italic"> <small
{i18n.t('locked')} className="unselectable pointer ml-2 text-muted font-italic"
data-tippy-content={i18n.t('locked')}
>
<svg class={`icon icon-inline text-danger`}>
<use xlinkHref="#icon-lock"></use>
</svg>
</small> </small>
)} )}
{post.stickied && ( {post.stickied && (
<small className="ml-2 text-muted font-italic"> <small
{i18n.t('stickied')} className="unselectable pointer ml-2 text-muted font-italic"
data-tippy-content={i18n.t('stickied')}
>
<svg class={`icon icon-inline text-success`}>
<use xlinkHref="#icon-pin"></use>
</svg>
</small> </small>
)} )}
{post.nsfw && ( {post.nsfw && (
@ -398,7 +425,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<ul class="list-inline mb-0 text-muted small"> <ul class="list-inline mb-0 text-muted small">
<li className="list-inline-item"> <li className="list-inline-item">
<span>{i18n.t('by')} </span> <span>{i18n.t('by')} </span>
<Link className="text-info" to={`/u/${post.creator_name}`}> <Link
className="text-body font-weight-bold"
to={`/u/${post.creator_name}`}
>
{post.creator_avatar && showAvatars() && ( {post.creator_avatar && showAvatars() && (
<img <img
height="32" height="32"
@ -433,28 +463,50 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
</span> </span>
)} )}
</li> </li>
<li className="list-inline-item"></li>
<li className="list-inline-item"> <li className="list-inline-item">
<span> <span>
<MomentTime data={post} /> <MomentTime data={post} />
</span> </span>
</li> </li>
<li className="list-inline-item"></li>
{this.state.upvotes !== this.state.score && (
<>
<li className="list-inline-item">
<span className="text-muted">
<svg class="small icon icon-inline mr-1">
<use xlinkHref="#icon-arrow-up"></use>
</svg>
{this.state.upvotes}
</span>
</li>
<li className="list-inline-item">
<span className="text-muted">
<svg class="small icon icon-inline mr-1">
<use xlinkHref="#icon-arrow-down"></use>
</svg>
{this.state.downvotes}
</span>
</li>
<li className="list-inline-item"></li>
</>
)}
<li className="list-inline-item"> <li className="list-inline-item">
<span> <Link
(<span className="text-info">+{this.state.upvotes}</span> className="text-muted"
<span> | </span> title={i18n.t('number_of_comments', {
<span className="text-danger">-{this.state.downvotes}</span>
<span>) </span>
</span>
</li>
<li className="list-inline-item">
<Link className="text-muted" to={`/post/${post.id}`}>
{i18n.t('number_of_comments', {
count: post.number_of_comments, count: post.number_of_comments,
})} })}
to={`/post/${post.id}`}
>
<svg class="mr-1 icon icon-inline">
<use xlinkHref="#icon-message-square"></use>
</svg>
{post.number_of_comments}
</Link> </Link>
</li> </li>
</ul> </ul>
<ul class="list-inline mb-1 text-muted small"> <ul class="list-inline mb-1 small text-muted">
{this.props.post.duplicates && ( {this.props.post.duplicates && (
<> <>
<li className="list-inline-item mr-2"> <li className="list-inline-item mr-2">
@ -470,93 +522,140 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
</> </>
)} )}
</ul> </ul>
<ul class="list-inline mb-1 text-muted small font-weight-bold"> <ul class="list-inline mb-1 text-muted h5 font-weight-bold">
{UserService.Instance.user && ( {UserService.Instance.user && (
<> <>
{this.props.showBody && ( {this.props.showBody && (
<> <>
<li className="list-inline-item mr-2"> <li className="list-inline-item-action">
<span <span
class="pointer" class="pointer"
onClick={linkEvent(this, this.handleSavePostClick)} onClick={linkEvent(this, this.handleSavePostClick)}
data-tippy-content={
post.saved ? i18n.t('unsave') : i18n.t('save')
}
> >
{post.saved ? i18n.t('unsave') : i18n.t('save')} <svg
class={`icon icon-inline ${post.saved &&
'text-warning'}`}
>
<use xlinkHref="#icon-star"></use>
</svg>
</span> </span>
</li> </li>
<li className="list-inline-item mr-2"> <li className="list-inline-item-action">
<Link <Link
className="text-muted" className="text-muted"
to={`/create_post${this.crossPostParams}`} to={`/create_post${this.crossPostParams}`}
title={i18n.t('cross_post')}
> >
{i18n.t('cross_post')} <svg class="icon icon-inline">
<use xlinkHref="#icon-copy"></use>
</svg>
</Link> </Link>
</li> </li>
</> </>
)} )}
{this.myPost && this.props.showBody && ( {this.myPost && this.props.showBody && (
<> <>
<li className="list-inline-item"> <li className="list-inline-item-action">
<span <span
class="pointer" class="pointer"
onClick={linkEvent(this, this.handleEditClick)} onClick={linkEvent(this, this.handleEditClick)}
data-tippy-content={i18n.t('edit')}
> >
{i18n.t('edit')} <svg class="icon icon-inline">
<use xlinkHref="#icon-edit"></use>
</svg>
</span> </span>
</li> </li>
<li className="list-inline-item mr-2"> <li className="list-inline-item-action">
<span <span
class="pointer" class="pointer"
onClick={linkEvent(this, this.handleDeleteClick)} onClick={linkEvent(this, this.handleDeleteClick)}
data-tippy-content={
!post.deleted
? i18n.t('delete')
: i18n.t('restore')
}
> >
{!post.deleted <svg
? i18n.t('delete') class={`icon icon-inline ${post.deleted &&
: i18n.t('restore')} 'text-danger'}`}
>
<use xlinkHref="#icon-trash"></use>
</svg>
</span> </span>
</li> </li>
</> </>
)} )}
{!this.state.showAdvanced && this.props.showBody ? ( {!this.state.showAdvanced && this.props.showBody ? (
<li className="list-inline-item"> <li className="list-inline-item-action">
<span <span
className="pointer" className="pointer"
onClick={linkEvent(this, this.handleShowAdvanced)} onClick={linkEvent(this, this.handleShowAdvanced)}
data-tippy-content={i18n.t('more')}
> >
{i18n.t('more')} <svg class="icon icon-inline">
<use xlinkHref="#icon-more-vertical"></use>
</svg>
</span> </span>
</li> </li>
) : ( ) : (
<> <>
{this.props.showBody && post.body && ( {this.props.showBody && post.body && (
<li className="list-inline-item"> <li className="list-inline-item-action">
<span <span
className="pointer" className="pointer"
onClick={linkEvent(this, this.handleViewSource)} onClick={linkEvent(this, this.handleViewSource)}
data-tippy-content={i18n.t('view_source')}
> >
{i18n.t('view_source')} <svg
class={`icon icon-inline ${this.state
.viewSource && 'text-success'}`}
>
<use xlinkHref="#icon-file-text"></use>
</svg>
</span> </span>
</li> </li>
)} )}
{this.canModOnSelf && ( {this.canModOnSelf && (
<> <>
<li className="list-inline-item"> <li className="list-inline-item-action">
<span <span
class="pointer" class="pointer"
onClick={linkEvent(this, this.handleModLock)} onClick={linkEvent(this, this.handleModLock)}
data-tippy-content={
post.locked
? i18n.t('unlock')
: i18n.t('lock')
}
> >
{post.locked <svg
? i18n.t('unlock') class={`icon icon-inline ${post.locked &&
: i18n.t('lock')} 'text-danger'}`}
>
<use xlinkHref="#icon-lock"></use>
</svg>
</span> </span>
</li> </li>
<li className="list-inline-item"> <li className="list-inline-item-action">
<span <span
class="pointer" class="pointer"
onClick={linkEvent(this, this.handleModSticky)} onClick={linkEvent(this, this.handleModSticky)}
data-tippy-content={
post.stickied
? i18n.t('unsticky')
: i18n.t('sticky')
}
> >
{post.stickied <svg
? i18n.t('unsticky') class={`icon icon-inline ${post.stickied &&
: i18n.t('sticky')} 'text-success'}`}
>
<use xlinkHref="#icon-pin"></use>
</svg>
</span> </span>
</li> </li>
</> </>
@ -1236,5 +1335,6 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
handleShowAdvanced(i: PostListing) { handleShowAdvanced(i: PostListing) {
i.state.showAdvanced = !i.state.showAdvanced; i.state.showAdvanced = !i.state.showAdvanced;
i.setState(i.state); i.setState(i.state);
setupTippy();
} }
} }

View file

@ -53,7 +53,7 @@ export class PostListings extends Component<PostListingsProps, any> {
} }
if (this.props.sort !== undefined) { if (this.props.sort !== undefined) {
postSort(out, this.props.sort); postSort(out, this.props.sort, this.props.showCommunity == undefined);
} }
return out; return out;

View file

@ -37,6 +37,7 @@ import {
createCommentLikeRes, createCommentLikeRes,
createPostLikeRes, createPostLikeRes,
commentsToFlatNodes, commentsToFlatNodes,
setupTippy,
} from '../utils'; } from '../utils';
import { PostListing } from './post-listing'; import { PostListing } from './post-listing';
import { PostListings } from './post-listings'; import { PostListings } from './post-listings';
@ -210,7 +211,7 @@ export class Post extends Component<any, PostState> {
sortRadios() { sortRadios() {
return ( return (
<div class="btn-group btn-group-toggle mb-3"> <div class="btn-group btn-group-toggle">
<label <label
className={`btn btn-sm btn-secondary pointer ${this.state className={`btn btn-sm btn-secondary pointer ${this.state
.commentSort === CommentSortType.Hot && 'active'}`} .commentSort === CommentSortType.Hot && 'active'}`}
@ -310,16 +311,27 @@ export class Post extends Component<any, PostState> {
} }
let tree: Array<CommentNodeI> = []; let tree: Array<CommentNodeI> = [];
for (let comment of this.state.comments) { for (let comment of this.state.comments) {
let child = map.get(comment.id);
if (comment.parent_id) { if (comment.parent_id) {
map.get(comment.parent_id).children.push(map.get(comment.id)); let parent_ = map.get(comment.parent_id);
parent_.children.push(child);
} else { } else {
tree.push(map.get(comment.id)); tree.push(child);
} }
this.setDepth(child);
} }
return tree; return tree;
} }
setDepth(node: CommentNodeI, i: number = 0): void {
for (let child of node.children) {
child.comment.depth = i;
this.setDepth(child, i + 1);
}
}
commentsTree() { commentsTree() {
let nodes = this.buildCommentsTree(); let nodes = this.buildCommentsTree();
return ( return (
@ -370,6 +382,7 @@ export class Post extends Component<any, PostState> {
} }
this.setState(this.state); this.setState(this.state);
setupTippy();
} else if (res.op == UserOperation.CreateComment) { } else if (res.op == UserOperation.CreateComment) {
let data = res.data as CommentResponse; let data = res.data as CommentResponse;
@ -386,6 +399,7 @@ export class Post extends Component<any, PostState> {
let data = res.data as CommentResponse; let data = res.data as CommentResponse;
saveCommentRes(data, this.state.comments); saveCommentRes(data, this.state.comments);
this.setState(this.state); this.setState(this.state);
setupTippy();
} else if (res.op == UserOperation.CreateCommentLike) { } else if (res.op == UserOperation.CreateCommentLike) {
let data = res.data as CommentResponse; let data = res.data as CommentResponse;
createCommentLikeRes(data, this.state.comments); createCommentLikeRes(data, this.state.comments);
@ -398,10 +412,12 @@ export class Post extends Component<any, PostState> {
let data = res.data as PostResponse; let data = res.data as PostResponse;
this.state.post = data.post; this.state.post = data.post;
this.setState(this.state); this.setState(this.state);
setupTippy();
} else if (res.op == UserOperation.SavePost) { } else if (res.op == UserOperation.SavePost) {
let data = res.data as PostResponse; let data = res.data as PostResponse;
this.state.post = data.post; this.state.post = data.post;
this.setState(this.state); this.setState(this.state);
setupTippy();
} else if (res.op == UserOperation.EditCommunity) { } else if (res.op == UserOperation.EditCommunity) {
let data = res.data as CommunityResponse; let data = res.data as CommunityResponse;
this.state.community = data.community; this.state.community = data.community;

View file

@ -126,7 +126,7 @@ export class PrivateMessageForm extends Component<
{this.state.recipient && ( {this.state.recipient && (
<div class="col-sm-10 form-control-plaintext"> <div class="col-sm-10 form-control-plaintext">
<Link <Link
className="text-info" className="text-body font-weight-bold"
to={`/u/${this.state.recipient.name}`} to={`/u/${this.state.recipient.name}`}
> >
{this.state.recipient.avatar && showAvatars() && ( {this.state.recipient.avatar && showAvatars() && (

View file

@ -84,26 +84,37 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
<Link className="text-muted" to={`/c/${community.name}`}> <Link className="text-muted" to={`/c/${community.name}`}>
/c/{community.name} /c/{community.name}
</Link> </Link>
<ul class="list-inline mb-1 text-muted small font-weight-bold"> <ul class="list-inline mb-1 text-muted font-weight-bold">
{this.canMod && ( {this.canMod && (
<> <>
<li className="list-inline-item"> <li className="list-inline-item-action">
<span <span
class="pointer" class="pointer"
onClick={linkEvent(this, this.handleEditClick)} onClick={linkEvent(this, this.handleEditClick)}
data-tippy-content={i18n.t('edit')}
> >
{i18n.t('edit')} <svg class="icon icon-inline">
<use xlinkHref="#icon-edit"></use>
</svg>
</span> </span>
</li> </li>
{this.amCreator && ( {this.amCreator && (
<li className="list-inline-item"> <li className="list-inline-item-action">
<span <span
class="pointer" class="pointer"
onClick={linkEvent(this, this.handleDeleteClick)} onClick={linkEvent(this, this.handleDeleteClick)}
data-tippy-content={
!community.deleted
? i18n.t('delete')
: i18n.t('restore')
}
> >
{!community.deleted <svg
? i18n.t('delete') class={`icon icon-inline ${community.deleted &&
: i18n.t('restore')} 'text-danger'}`}
>
<use xlinkHref="#icon-trash"></use>
</svg>
</span> </span>
</li> </li>
)} )}
@ -193,7 +204,10 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
<li class="list-inline-item">{i18n.t('mods')}: </li> <li class="list-inline-item">{i18n.t('mods')}: </li>
{this.props.moderators.map(mod => ( {this.props.moderators.map(mod => (
<li class="list-inline-item"> <li class="list-inline-item">
<Link class="text-info" to={`/u/${mod.user_name}`}> <Link
class="text-body font-weight-bold"
to={`/u/${mod.user_name}`}
>
{mod.avatar && showAvatars() && ( {mod.avatar && showAvatars() && (
<img <img
height="32" height="32"

View file

@ -15,40 +15,87 @@ export class Symbols extends Component<any, any> {
xmlnsXlink="http://www.w3.org/1999/xlink" xmlnsXlink="http://www.w3.org/1999/xlink"
> >
<defs> <defs>
<symbol id="icon-zap" viewBox="0 0 24 24">
<path d="M11.585 5.26l-0.577 4.616c0.033 0.716 0.465 1.124 0.992 1.124h6.865l-6.45 7.74 0.577-4.616c-0.033-0.716-0.465-1.124-0.992-1.124h-6.865zM12.232 1.36l-10 12c-0.354 0.424-0.296 1.055 0.128 1.408 0.187 0.157 0.415 0.233 0.64 0.232h7.867l-0.859 6.876c-0.069 0.548 0.32 1.048 0.868 1.116 0.349 0.044 0.678-0.098 0.892-0.352l10-12c0.354-0.424 0.296-1.055-0.128-1.408-0.187-0.157-0.415-0.233-0.64-0.232h-7.867l0.859-6.876c0.069-0.548-0.32-1.048-0.868-1.116-0.349-0.044-0.678 0.098-0.892 0.352z"></path>
</symbol>
<symbol id="icon-heart" viewBox="0 0 24 24">
<path d="M20.133 5.317c0.88 0.881 1.319 2.031 1.319 3.184s-0.44 2.303-1.319 3.182l-8.133 8.133-8.133-8.133c-0.879-0.879-1.318-2.029-1.318-3.183s0.439-2.304 1.318-3.183 2.029-1.318 3.183-1.318 2.304 0.439 3.183 1.318l1.060 1.060c0.391 0.391 1.024 0.391 1.414 0l1.062-1.062c0.879-0.879 2.029-1.318 3.182-1.317s2.303 0.44 3.182 1.319zM21.547 3.903c-1.269-1.269-2.934-1.904-4.596-1.905s-3.327 0.634-4.597 1.903l-0.354 0.355-0.353-0.353c-1.269-1.269-2.935-1.904-4.597-1.904s-3.328 0.635-4.597 1.904-1.904 2.935-1.904 4.597 0.635 3.328 1.904 4.597l8.84 8.84c0.391 0.391 1.024 0.391 1.414 0l8.84-8.84c1.269-1.269 1.904-2.934 1.905-4.596s-0.634-3.327-1.905-4.598z"></path>
</symbol>
<symbol id="icon-link" viewBox="0 0 24 24">
<path d="M9.199 13.599c0.992 1.327 2.43 2.126 3.948 2.345s3.123-0.142 4.45-1.134c0.239-0.179 0.465-0.375 0.655-0.568l2.995-2.995c1.163-1.204 1.722-2.751 1.696-4.285s-0.639-3.061-1.831-4.211c-1.172-1.132-2.688-1.692-4.199-1.683-1.492 0.008-2.984 0.571-4.137 1.683l-1.731 1.721c-0.392 0.389-0.394 1.023-0.004 1.414s1.023 0.394 1.414 0.004l1.709-1.699c0.77-0.742 1.763-1.117 2.76-1.123 1.009-0.006 2.016 0.367 2.798 1.122 0.795 0.768 1.203 1.783 1.221 2.808s-0.355 2.054-1.11 2.836l-3.005 3.005c-0.114 0.116-0.263 0.247-0.428 0.37-0.885 0.662-1.952 0.902-2.967 0.756s-1.971-0.678-2.632-1.563c-0.331-0.442-0.957-0.533-1.4-0.202s-0.533 0.957-0.202 1.4zM14.801 10.401c-0.992-1.327-2.43-2.126-3.948-2.345s-3.124 0.142-4.451 1.134c-0.239 0.179-0.464 0.375-0.655 0.568l-2.995 2.995c-1.163 1.204-1.722 2.751-1.696 4.285s0.639 3.061 1.831 4.211c1.172 1.132 2.688 1.692 4.199 1.683 1.492-0.008 2.984-0.571 4.137-1.683l1.723-1.723c0.391-0.391 0.391-1.024 0-1.414s-1.024-0.391-1.414 0l-1.696 1.698c-0.77 0.742-1.763 1.117-2.76 1.123-1.009 0.006-2.016-0.367-2.798-1.122-0.795-0.768-1.203-1.783-1.221-2.808s0.355-2.054 1.11-2.836l3.005-3.005c0.114-0.116 0.263-0.247 0.428-0.37 0.885-0.662 1.952-0.902 2.967-0.756s1.971 0.678 2.632 1.563c0.331 0.442 0.957 0.533 1.4 0.202s0.533-0.957 0.202-1.4z"></path>
</symbol>
<symbol id="icon-minus-square" viewBox="0 0 24 24">
<path d="M5 2c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v14c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h14c0.828 0 1.58-0.337 2.121-0.879s0.879-1.293 0.879-2.121v-14c0-0.828-0.337-1.58-0.879-2.121s-1.293-0.879-2.121-0.879zM5 4h14c0.276 0 0.525 0.111 0.707 0.293s0.293 0.431 0.293 0.707v14c0 0.276-0.111 0.525-0.293 0.707s-0.431 0.293-0.707 0.293h-14c-0.276 0-0.525-0.111-0.707-0.293s-0.293-0.431-0.293-0.707v-14c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293zM8 13h8c0.552 0 1-0.448 1-1s-0.448-1-1-1h-8c-0.552 0-1 0.448-1 1s0.448 1 1 1z"></path>
</symbol>
<symbol id="icon-plus-square" viewBox="0 0 24 24">
<path d="M5 2c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v14c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h14c0.828 0 1.58-0.337 2.121-0.879s0.879-1.293 0.879-2.121v-14c0-0.828-0.337-1.58-0.879-2.121s-1.293-0.879-2.121-0.879zM5 4h14c0.276 0 0.525 0.111 0.707 0.293s0.293 0.431 0.293 0.707v14c0 0.276-0.111 0.525-0.293 0.707s-0.431 0.293-0.707 0.293h-14c-0.276 0-0.525-0.111-0.707-0.293s-0.293-0.431-0.293-0.707v-14c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293zM8 13h3v3c0 0.552 0.448 1 1 1s1-0.448 1-1v-3h3c0.552 0 1-0.448 1-1s-0.448-1-1-1h-3v-3c0-0.552-0.448-1-1-1s-1 0.448-1 1v3h-3c-0.552 0-1 0.448-1 1s0.448 1 1 1z"></path>
</symbol>
<symbol id="icon-help-circle" viewBox="0 0 24 24">
<path d="M23 12c0-3.037-1.232-5.789-3.222-7.778s-4.741-3.222-7.778-3.222-5.789 1.232-7.778 3.222-3.222 4.741-3.222 7.778 1.232 5.789 3.222 7.778 4.741 3.222 7.778 3.222 5.789-1.232 7.778-3.222 3.222-4.741 3.222-7.778zM21 12c0 2.486-1.006 4.734-2.636 6.364s-3.878 2.636-6.364 2.636-4.734-1.006-6.364-2.636-2.636-3.878-2.636-6.364 1.006-4.734 2.636-6.364 3.878-2.636 6.364-2.636 4.734 1.006 6.364 2.636 2.636 3.878 2.636 6.364zM10.033 9.332c0.183-0.521 0.559-0.918 1.022-1.14s1.007-0.267 1.528-0.083c0.458 0.161 0.819 0.47 1.050 0.859 0.183 0.307 0.284 0.665 0.286 1.037 0 0.155-0.039 0.309-0.117 0.464-0.080 0.16-0.203 0.325-0.368 0.49-0.709 0.709-1.831 1.092-1.831 1.092-0.524 0.175-0.807 0.741-0.632 1.265s0.741 0.807 1.265 0.632c0 0 1.544-0.506 2.613-1.575 0.279-0.279 0.545-0.614 0.743-1.010 0.2-0.4 0.328-0.858 0.328-1.369-0.004-0.731-0.204-1.437-0.567-2.049-0.463-0.778-1.19-1.402-2.105-1.724-1.042-0.366-2.135-0.275-3.057 0.167s-1.678 1.238-2.044 2.28c-0.184 0.521 0.090 1.092 0.611 1.275s1.092-0.091 1.275-0.611zM12 18c0.552 0 1-0.448 1-1s-0.448-1-1-1-1 0.448-1 1 0.448 1 1 1z"></path>
</symbol>
<symbol id="icon-pin" viewBox="0 0 18 18">
<path d="M15 2v-1h-12v1c0 0.552 0.448 1 1 1v8c-0.552 0-1 0.448-1 1v1h5v3c0 0.552 0.448 1 1 1s1-0.448 1-1v-3h5v-1c0-0.552-0.448-1-1-1v-8c0.552 0 1-0.448 1-1zM12 11h-6v-8h6v8z"></path>
</symbol>
<symbol id="icon-lock" viewBox="0 0 24 24">
<path d="M5 12h14c0.276 0 0.525 0.111 0.707 0.293s0.293 0.431 0.293 0.707v7c0 0.276-0.111 0.525-0.293 0.707s-0.431 0.293-0.707 0.293h-14c-0.276 0-0.525-0.111-0.707-0.293s-0.293-0.431-0.293-0.707v-7c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293zM18 10v-3c0-1.657-0.673-3.158-1.757-4.243s-2.586-1.757-4.243-1.757-3.158 0.673-4.243 1.757-1.757 2.586-1.757 4.243v3h-1c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v7c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h14c0.828 0 1.58-0.337 2.121-0.879s0.879-1.293 0.879-2.121v-7c0-0.828-0.337-1.58-0.879-2.121s-1.293-0.879-2.121-0.879zM8 10v-3c0-1.105 0.447-2.103 1.172-2.828s1.723-1.172 2.828-1.172 2.103 0.447 2.828 1.172 1.172 1.723 1.172 2.828v3z"></path>
</symbol>
<symbol id="icon-check" viewBox="0 0 24 24">
<path d="M19.293 5.293l-10.293 10.293-4.293-4.293c-0.391-0.391-1.024-0.391-1.414 0s-0.391 1.024 0 1.414l5 5c0.391 0.391 1.024 0.391 1.414 0l11-11c0.391-0.391 0.391-1.024 0-1.414s-1.024-0.391-1.414 0z"></path>
</symbol>
<symbol id="icon-copy" viewBox="0 0 24 24">
<path d="M11 8c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v9c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h9c0.828 0 1.58-0.337 2.121-0.879s0.879-1.293 0.879-2.121v-9c0-0.828-0.337-1.58-0.879-2.121s-1.293-0.879-2.121-0.879zM11 10h9c0.276 0 0.525 0.111 0.707 0.293s0.293 0.431 0.293 0.707v9c0 0.276-0.111 0.525-0.293 0.707s-0.431 0.293-0.707 0.293h-9c-0.276 0-0.525-0.111-0.707-0.293s-0.293-0.431-0.293-0.707v-9c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293zM5 14h-1c-0.276 0-0.525-0.111-0.707-0.293s-0.293-0.431-0.293-0.707v-9c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293h9c0.276 0 0.525 0.111 0.707 0.293s0.293 0.431 0.293 0.707v1c0 0.552 0.448 1 1 1s1-0.448 1-1v-1c0-0.828-0.337-1.58-0.879-2.121s-1.293-0.879-2.121-0.879h-9c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v9c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h1c0.552 0 1-0.448 1-1s-0.448-1-1-1z"></path>
</symbol>
<symbol id="icon-more-vertical" viewBox="0 0 24 24">
<path d="M14 12c0-0.552-0.225-1.053-0.586-1.414s-0.862-0.586-1.414-0.586-1.053 0.225-1.414 0.586-0.586 0.862-0.586 1.414 0.225 1.053 0.586 1.414 0.862 0.586 1.414 0.586 1.053-0.225 1.414-0.586 0.586-0.862 0.586-1.414zM14 5c0-0.552-0.225-1.053-0.586-1.414s-0.862-0.586-1.414-0.586-1.053 0.225-1.414 0.586-0.586 0.862-0.586 1.414 0.225 1.053 0.586 1.414 0.862 0.586 1.414 0.586 1.053-0.225 1.414-0.586 0.586-0.862 0.586-1.414zM14 19c0-0.552-0.225-1.053-0.586-1.414s-0.862-0.586-1.414-0.586-1.053 0.225-1.414 0.586-0.586 0.862-0.586 1.414 0.225 1.053 0.586 1.414 0.862 0.586 1.414 0.586 1.053-0.225 1.414-0.586 0.586-0.862 0.586-1.414z"></path>
</symbol>
<symbol id="icon-bell" viewBox="0 0 24 24">
<path d="M17 8c0 4.011 0.947 6.52 1.851 8h-13.702c0.904-1.48 1.851-3.989 1.851-8 0-1.381 0.559-2.63 1.464-3.536s2.155-1.464 3.536-1.464 2.63 0.559 3.536 1.464 1.464 2.155 1.464 3.536zM19 8c0-1.933-0.785-3.684-2.050-4.95s-3.017-2.050-4.95-2.050-3.684 0.785-4.95 2.050-2.050 3.017-2.050 4.95c0 6.127-2.393 8.047-2.563 8.174-0.453 0.308-0.573 0.924-0.269 1.381 0.192 0.287 0.506 0.443 0.832 0.445h18c0.552 0 1-0.448 1-1 0-0.339-0.168-0.638-0.429-0.821-0.176-0.13-2.571-2.050-2.571-8.179zM12.865 20.498c-0.139 0.239-0.359 0.399-0.608 0.465s-0.52 0.037-0.759-0.101c-0.162-0.094-0.283-0.222-0.359-0.357-0.274-0.48-0.884-0.647-1.364-0.373s-0.647 0.884-0.373 1.364c0.25 0.439 0.623 0.823 1.093 1.096 0.716 0.416 1.535 0.501 2.276 0.304s1.409-0.678 1.824-1.394c0.277-0.478 0.114-1.090-0.363-1.367s-1.090-0.114-1.367 0.363z"></path>
</symbol>
<symbol id="icon-file-text" viewBox="0 0 24 24">
<path d="M17.586 7h-2.586v-2.586zM20.707 7.293l-6-6c-0.092-0.092-0.202-0.166-0.324-0.217s-0.253-0.076-0.383-0.076h-8c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v16c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h12c0.828 0 1.58-0.337 2.121-0.879s0.879-1.293 0.879-2.121v-12c0-0.276-0.112-0.526-0.293-0.707zM13 3v5c0 0.552 0.448 1 1 1h5v11c0 0.276-0.111 0.525-0.293 0.707s-0.431 0.293-0.707 0.293h-12c-0.276 0-0.525-0.111-0.707-0.293s-0.293-0.431-0.293-0.707v-16c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293zM16 12h-8c-0.552 0-1 0.448-1 1s0.448 1 1 1h8c0.552 0 1-0.448 1-1s-0.448-1-1-1zM16 16h-8c-0.552 0-1 0.448-1 1s0.448 1 1 1h8c0.552 0 1-0.448 1-1s-0.448-1-1-1zM10 8h-2c-0.552 0-1 0.448-1 1s0.448 1 1 1h2c0.552 0 1-0.448 1-1s-0.448-1-1-1z"></path>
</symbol>
<symbol id="icon-eye" viewBox="0 0 24 24">
<path d="M0.106 11.553c-0.136 0.274-0.146 0.603 0 0.894 0 0 0.396 0.789 1.12 1.843 0.451 0.656 1.038 1.432 1.757 2.218 0.894 0.979 2.004 1.987 3.319 2.8 1.595 0.986 3.506 1.692 5.698 1.692s4.103-0.706 5.698-1.692c1.315-0.813 2.425-1.821 3.319-2.8 0.718-0.786 1.306-1.562 1.757-2.218 0.724-1.054 1.12-1.843 1.12-1.843 0.136-0.274 0.146-0.603 0-0.894 0 0-0.396-0.789-1.12-1.843-0.451-0.656-1.038-1.432-1.757-2.218-0.894-0.979-2.004-1.987-3.319-2.8-1.595-0.986-3.506-1.692-5.698-1.692s-4.103 0.706-5.698 1.692c-1.315 0.813-2.425 1.821-3.319 2.8-0.719 0.786-1.306 1.561-1.757 2.218-0.724 1.054-1.12 1.843-1.12 1.843zM2.14 12c0.163-0.281 0.407-0.681 0.734-1.158 0.41-0.596 0.94-1.296 1.585-2.001 0.805-0.881 1.775-1.756 2.894-2.448 1.35-0.834 2.901-1.393 4.647-1.393s3.297 0.559 4.646 1.393c1.119 0.692 2.089 1.567 2.894 2.448 0.644 0.705 1.175 1.405 1.585 2.001 0.328 0.477 0.572 0.876 0.734 1.158-0.163 0.281-0.407 0.681-0.734 1.158-0.41 0.596-0.94 1.296-1.585 2.001-0.805 0.881-1.775 1.756-2.894 2.448-1.349 0.834-2.9 1.393-4.646 1.393s-3.297-0.559-4.646-1.393c-1.119-0.692-2.089-1.567-2.894-2.448-0.644-0.705-1.175-1.405-1.585-2.001-0.328-0.477-0.572-0.877-0.735-1.158zM16 12c0-1.104-0.449-2.106-1.172-2.828s-1.724-1.172-2.828-1.172-2.106 0.449-2.828 1.172-1.172 1.724-1.172 2.828 0.449 2.106 1.172 2.828 1.724 1.172 2.828 1.172 2.106-0.449 2.828-1.172 1.172-1.724 1.172-2.828zM14 12c0 0.553-0.223 1.051-0.586 1.414s-0.861 0.586-1.414 0.586-1.051-0.223-1.414-0.586-0.586-0.861-0.586-1.414 0.223-1.051 0.586-1.414 0.861-0.586 1.414-0.586 1.051 0.223 1.414 0.586 0.586 0.861 0.586 1.414z"></path>
</symbol>
<symbol id="icon-edit" viewBox="0 0 24 24">
<path d="M11 3h-7c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v14c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h14c0.828 0 1.58-0.337 2.121-0.879s0.879-1.293 0.879-2.121v-7c0-0.552-0.448-1-1-1s-1 0.448-1 1v7c0 0.276-0.111 0.525-0.293 0.707s-0.431 0.293-0.707 0.293h-14c-0.276 0-0.525-0.111-0.707-0.293s-0.293-0.431-0.293-0.707v-14c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293h7c0.552 0 1-0.448 1-1s-0.448-1-1-1zM17.793 1.793l-9.5 9.5c-0.122 0.121-0.217 0.28-0.263 0.465l-1 4c-0.039 0.15-0.042 0.318 0 0.485 0.134 0.536 0.677 0.862 1.213 0.728l4-1c0.167-0.041 0.33-0.129 0.465-0.263l9.5-9.5c0.609-0.609 0.914-1.41 0.914-2.207s-0.305-1.598-0.914-2.207-1.411-0.915-2.208-0.915-1.598 0.305-2.207 0.914zM19.207 3.207c0.219-0.219 0.504-0.328 0.793-0.328s0.574 0.109 0.793 0.328 0.328 0.504 0.328 0.793-0.109 0.574-0.328 0.793l-9.304 9.304-2.114 0.529 0.529-2.114z"></path>
</symbol>
<symbol id="icon-edit-2" viewBox="0 0 24 24">
<path d="M16.293 2.293l-13.5 13.5c-0.117 0.116-0.21 0.268-0.258 0.444l-1.5 5.5c-0.046 0.163-0.049 0.346 0 0.526 0.145 0.533 0.695 0.847 1.228 0.702l5.5-1.5c0.159-0.042 0.315-0.129 0.444-0.258l13.5-13.5c0.747-0.747 1.121-1.729 1.121-2.707s-0.374-1.96-1.121-2.707-1.729-1.121-2.707-1.121-1.96 0.374-2.707 1.121zM17.707 3.707c0.357-0.357 0.824-0.535 1.293-0.535s0.936 0.178 1.293 0.536 0.535 0.823 0.535 1.292-0.178 0.936-0.535 1.293l-13.312 13.312-3.556 0.97 0.97-3.555z"></path>
</symbol>
<symbol id="icon-trash" viewBox="0 0 24 24">
<path d="M18 7v13c0 0.276-0.111 0.525-0.293 0.707s-0.431 0.293-0.707 0.293h-10c-0.276 0-0.525-0.111-0.707-0.293s-0.293-0.431-0.293-0.707v-13zM17 5v-1c0-0.828-0.337-1.58-0.879-2.121s-1.293-0.879-2.121-0.879h-4c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v1h-4c-0.552 0-1 0.448-1 1s0.448 1 1 1h1v13c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h10c0.828 0 1.58-0.337 2.121-0.879s0.879-1.293 0.879-2.121v-13h1c0.552 0 1-0.448 1-1s-0.448-1-1-1zM9 5v-1c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293h4c0.276 0 0.525 0.111 0.707 0.293s0.293 0.431 0.293 0.707v1z"></path>
</symbol>
<symbol id="icon-reply1" viewBox="0 0 20 20">
<path d="M19 16.685c0 0-2.225-9.732-11-9.732v-3.984l-7 6.573 7 6.69v-4.357c4.763-0.001 8.516 0.421 11 4.81z"></path>
</symbol>
<symbol id="icon-star" viewBox="0 0 24 24">
<path d="M12.897 1.557c-0.092-0.189-0.248-0.352-0.454-0.454-0.495-0.244-1.095-0.041-1.339 0.454l-2.858 5.789-6.391 0.935c-0.208 0.029-0.411 0.127-0.571 0.291-0.386 0.396-0.377 1.029 0.018 1.414l4.623 4.503-1.091 6.362c-0.036 0.207-0.006 0.431 0.101 0.634 0.257 0.489 0.862 0.677 1.351 0.42l5.714-3.005 5.715 3.005c0.186 0.099 0.408 0.139 0.634 0.101 0.544-0.093 0.91-0.61 0.817-1.155l-1.091-6.362 4.623-4.503c0.151-0.146 0.259-0.344 0.292-0.572 0.080-0.546-0.298-1.054-0.845-1.134l-6.39-0.934zM12 4.259l2.193 4.444c0.151 0.305 0.436 0.499 0.752 0.547l4.906 0.717-3.549 3.457c-0.244 0.238-0.341 0.569-0.288 0.885l0.837 4.883-4.386-2.307c-0.301-0.158-0.647-0.148-0.931 0l-4.386 2.307 0.837-4.883c0.058-0.336-0.059-0.661-0.288-0.885l-3.549-3.457 4.907-0.718c0.336-0.049 0.609-0.26 0.752-0.546z"></path>
</symbol>
<symbol id="icon-message-square" viewBox="0 0 24 24"> <symbol id="icon-message-square" viewBox="0 0 24 24">
<title>message-square</title>
<path d="M22 15v-10c0-0.828-0.337-1.58-0.879-2.121s-1.293-0.879-2.121-0.879h-14c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v16c0 0.256 0.098 0.512 0.293 0.707 0.391 0.391 1.024 0.391 1.414 0l3.707-3.707h11.586c0.828 0 1.58-0.337 2.121-0.879s0.879-1.293 0.879-2.121zM20 15c0 0.276-0.111 0.525-0.293 0.707s-0.431 0.293-0.707 0.293h-12c-0.276 0-0.526 0.112-0.707 0.293l-2.293 2.293v-13.586c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293h14c0.276 0 0.525 0.111 0.707 0.293s0.293 0.431 0.293 0.707z"></path> <path d="M22 15v-10c0-0.828-0.337-1.58-0.879-2.121s-1.293-0.879-2.121-0.879h-14c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v16c0 0.256 0.098 0.512 0.293 0.707 0.391 0.391 1.024 0.391 1.414 0l3.707-3.707h11.586c0.828 0 1.58-0.337 2.121-0.879s0.879-1.293 0.879-2.121zM20 15c0 0.276-0.111 0.525-0.293 0.707s-0.431 0.293-0.707 0.293h-12c-0.276 0-0.526 0.112-0.707 0.293l-2.293 2.293v-13.586c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293h14c0.276 0 0.525 0.111 0.707 0.293s0.293 0.431 0.293 0.707z"></path>
</symbol> </symbol>
<symbol id="icon-image" viewBox="0 0 32 32"> <symbol id="icon-image" viewBox="0 0 24 24">
<title>image</title> <path d="M5 2c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v14c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h14c0.828 0 1.58-0.337 2.121-0.879s0.879-1.293 0.879-2.121v-14c0-0.828-0.337-1.58-0.879-2.121s-1.293-0.879-2.121-0.879zM11 8.5c0-0.69-0.281-1.316-0.732-1.768s-1.078-0.732-1.768-0.732-1.316 0.281-1.768 0.732-0.732 1.078-0.732 1.768 0.281 1.316 0.732 1.768 1.078 0.732 1.768 0.732 1.316-0.281 1.768-0.732 0.732-1.078 0.732-1.768zM9 8.5c0 0.138-0.055 0.262-0.146 0.354s-0.216 0.146-0.354 0.146-0.262-0.055-0.354-0.146-0.146-0.216-0.146-0.354 0.055-0.262 0.146-0.354 0.216-0.146 0.354-0.146 0.262 0.055 0.354 0.146 0.146 0.216 0.146 0.354zM7.414 20l8.586-8.586 4 4v3.586c0 0.276-0.111 0.525-0.293 0.707s-0.431 0.293-0.707 0.293zM20 12.586l-3.293-3.293c-0.391-0.391-1.024-0.391-1.414 0l-10.644 10.644c-0.135-0.050-0.255-0.129-0.356-0.23-0.182-0.182-0.293-0.431-0.293-0.707v-14c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293h14c0.276 0 0.525 0.111 0.707 0.293s0.293 0.431 0.293 0.707z"></path>
<path d="M29.996 4c0.001 0.001 0.003 0.002 0.004 0.004v23.993c-0.001 0.001-0.002 0.003-0.004 0.004h-27.993c-0.001-0.001-0.003-0.002-0.004-0.004v-23.993c0.001-0.001 0.002-0.003 0.004-0.004h27.993zM30 2h-28c-1.1 0-2 0.9-2 2v24c0 1.1 0.9 2 2 2h28c1.1 0 2-0.9 2-2v-24c0-1.1-0.9-2-2-2v0z"></path>
<path d="M26 9c0 1.657-1.343 3-3 3s-3-1.343-3-3 1.343-3 3-3 3 1.343 3 3z"></path>
<path d="M28 26h-24v-4l7-12 8 10h2l7-6z"></path>
</symbol> </symbol>
<symbol id="icon-external-link" viewBox="0 0 24 24"> <symbol id="icon-external-link" viewBox="0 0 24 24">
<title>external-link</title>
<path d="M17 13v6c0 0.276-0.111 0.525-0.293 0.707s-0.431 0.293-0.707 0.293h-11c-0.276 0-0.525-0.111-0.707-0.293s-0.293-0.431-0.293-0.707v-11c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293h6c0.552 0 1-0.448 1-1s-0.448-1-1-1h-6c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v11c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h11c0.828 0 1.58-0.337 2.121-0.879s0.879-1.293 0.879-2.121v-6c0-0.552-0.448-1-1-1s-1 0.448-1 1zM10.707 14.707l9.293-9.293v3.586c0 0.552 0.448 1 1 1s1-0.448 1-1v-6c0-0.136-0.027-0.265-0.076-0.383s-0.121-0.228-0.216-0.323c-0.001-0.001-0.001-0.001-0.002-0.002-0.092-0.092-0.202-0.166-0.323-0.216-0.118-0.049-0.247-0.076-0.383-0.076h-6c-0.552 0-1 0.448-1 1s0.448 1 1 1h3.586l-9.293 9.293c-0.391 0.391-0.391 1.024 0 1.414s1.024 0.391 1.414 0z"></path> <path d="M17 13v6c0 0.276-0.111 0.525-0.293 0.707s-0.431 0.293-0.707 0.293h-11c-0.276 0-0.525-0.111-0.707-0.293s-0.293-0.431-0.293-0.707v-11c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293h6c0.552 0 1-0.448 1-1s-0.448-1-1-1h-6c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v11c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h11c0.828 0 1.58-0.337 2.121-0.879s0.879-1.293 0.879-2.121v-6c0-0.552-0.448-1-1-1s-1 0.448-1 1zM10.707 14.707l9.293-9.293v3.586c0 0.552 0.448 1 1 1s1-0.448 1-1v-6c0-0.136-0.027-0.265-0.076-0.383s-0.121-0.228-0.216-0.323c-0.001-0.001-0.001-0.001-0.002-0.002-0.092-0.092-0.202-0.166-0.323-0.216-0.118-0.049-0.247-0.076-0.383-0.076h-6c-0.552 0-1 0.448-1 1s0.448 1 1 1h3.586l-9.293 9.293c-0.391 0.391-0.391 1.024 0 1.414s1.024 0.391 1.414 0z"></path>
</symbol> </symbol>
<symbol id="icon-coffee" viewBox="0 0 24 24"> <symbol id="icon-coffee" viewBox="0 0 24 24">
<title>coffee1</title>
<path d="M17 19h-12c-0.553 0-1-0.447-1-1s0.447-1 1-1h12c0.553 0 1 0.447 1 1s-0.447 1-1 1z"></path> <path d="M17 19h-12c-0.553 0-1-0.447-1-1s0.447-1 1-1h12c0.553 0 1 0.447 1 1s-0.447 1-1 1z"></path>
<path d="M17.5 5h-12.5v9c0 1.1 0.9 2 2 2h8c1.1 0 2-0.9 2-2v-2h0.5c1.93 0 3.5-1.57 3.5-3.5s-1.57-3.5-3.5-3.5zM15 14h-8v-7h8v7zM17.5 10h-1.5v-3h1.5c0.827 0 1.5 0.673 1.5 1.5s-0.673 1.5-1.5 1.5z"></path> <path d="M17.5 5h-12.5v9c0 1.1 0.9 2 2 2h8c1.1 0 2-0.9 2-2v-2h0.5c1.93 0 3.5-1.57 3.5-3.5s-1.57-3.5-3.5-3.5zM15 14h-8v-7h8v7zM17.5 10h-1.5v-3h1.5c0.827 0 1.5 0.673 1.5 1.5s-0.673 1.5-1.5 1.5z"></path>
</symbol> </symbol>
<symbol id="icon-rss" viewBox="0 0 32 32"> <symbol id="icon-rss" viewBox="0 0 32 32">
<title>rss</title>
<path d="M4.259 23.467c-2.35 0-4.259 1.917-4.259 4.252 0 2.349 1.909 4.244 4.259 4.244 2.358 0 4.265-1.895 4.265-4.244-0-2.336-1.907-4.252-4.265-4.252zM0.005 10.873v6.133c3.993 0 7.749 1.562 10.577 4.391 2.825 2.822 4.384 6.595 4.384 10.603h6.16c-0-11.651-9.478-21.127-21.121-21.127zM0.012 0v6.136c14.243 0 25.836 11.604 25.836 25.864h6.152c0-17.64-14.352-32-31.988-32z"></path> <path d="M4.259 23.467c-2.35 0-4.259 1.917-4.259 4.252 0 2.349 1.909 4.244 4.259 4.244 2.358 0 4.265-1.895 4.265-4.244-0-2.336-1.907-4.252-4.265-4.252zM0.005 10.873v6.133c3.993 0 7.749 1.562 10.577 4.391 2.825 2.822 4.384 6.595 4.384 10.603h6.16c-0-11.651-9.478-21.127-21.121-21.127zM0.012 0v6.136c14.243 0 25.836 11.604 25.836 25.864h6.152c0-17.64-14.352-32-31.988-32z"></path>
</symbol> </symbol>
<symbol id="icon-arrow-down" viewBox="0 0 26 28"> <symbol id="icon-arrow-down" viewBox="0 0 26 28">
<title>arrow-down</title>
<path d="M25.172 13c0 0.531-0.219 1.047-0.578 1.406l-10.172 10.187c-0.375 0.359-0.891 0.578-1.422 0.578s-1.047-0.219-1.406-0.578l-10.172-10.187c-0.375-0.359-0.594-0.875-0.594-1.406s0.219-1.047 0.594-1.422l1.156-1.172c0.375-0.359 0.891-0.578 1.422-0.578s1.047 0.219 1.406 0.578l4.594 4.594v-11c0-1.094 0.906-2 2-2h2c1.094 0 2 0.906 2 2v11l4.594-4.594c0.359-0.359 0.875-0.578 1.406-0.578s1.047 0.219 1.422 0.578l1.172 1.172c0.359 0.375 0.578 0.891 0.578 1.422z"></path> <path d="M25.172 13c0 0.531-0.219 1.047-0.578 1.406l-10.172 10.187c-0.375 0.359-0.891 0.578-1.422 0.578s-1.047-0.219-1.406-0.578l-10.172-10.187c-0.375-0.359-0.594-0.875-0.594-1.406s0.219-1.047 0.594-1.422l1.156-1.172c0.375-0.359 0.891-0.578 1.422-0.578s1.047 0.219 1.406 0.578l4.594 4.594v-11c0-1.094 0.906-2 2-2h2c1.094 0 2 0.906 2 2v11l4.594-4.594c0.359-0.359 0.875-0.578 1.406-0.578s1.047 0.219 1.422 0.578l1.172 1.172c0.359 0.375 0.578 0.891 0.578 1.422z"></path>
</symbol> </symbol>
<symbol id="icon-arrow-up" viewBox="0 0 26 28"> <symbol id="icon-arrow-up" viewBox="0 0 26 28">
<title>arrow-up</title>
<path d="M25.172 15.172c0 0.531-0.219 1.031-0.578 1.406l-1.172 1.172c-0.375 0.375-0.891 0.594-1.422 0.594s-1.047-0.219-1.406-0.594l-4.594-4.578v11c0 1.125-0.938 1.828-2 1.828h-2c-1.062 0-2-0.703-2-1.828v-11l-4.594 4.578c-0.359 0.375-0.875 0.594-1.406 0.594s-1.047-0.219-1.406-0.594l-1.172-1.172c-0.375-0.375-0.594-0.875-0.594-1.406s0.219-1.047 0.594-1.422l10.172-10.172c0.359-0.375 0.875-0.578 1.406-0.578s1.047 0.203 1.422 0.578l10.172 10.172c0.359 0.375 0.578 0.891 0.578 1.422z"></path> <path d="M25.172 15.172c0 0.531-0.219 1.031-0.578 1.406l-1.172 1.172c-0.375 0.375-0.891 0.594-1.422 0.594s-1.047-0.219-1.406-0.594l-4.594-4.578v11c0 1.125-0.938 1.828-2 1.828h-2c-1.062 0-2-0.703-2-1.828v-11l-4.594 4.578c-0.359 0.375-0.875 0.594-1.406 0.594s-1.047-0.219-1.406-0.594l-1.172-1.172c-0.375-0.375-0.594-0.875-0.594-1.406s0.219-1.047 0.594-1.422l10.172-10.172c0.359-0.375 0.875-0.578 1.406-0.578s1.047 0.203 1.422 0.578l10.172 10.172c0.359 0.375 0.578 0.891 0.578 1.422z"></path>
</symbol> </symbol>
<symbol id="icon-mail" viewBox="0 0 32 32"> <symbol id="icon-mail" viewBox="0 0 24 24">
<title>mail</title> <path d="M3 7.921l8.427 5.899c0.34 0.235 0.795 0.246 1.147 0l8.426-5.899v10.079c0 0.272-0.11 0.521-0.295 0.705s-0.433 0.295-0.705 0.295h-16c-0.272 0-0.521-0.11-0.705-0.295s-0.295-0.433-0.295-0.705zM1 5.983c0 0.010 0 0.020 0 0.030v11.987c0 0.828 0.34 1.579 0.88 2.12s1.292 0.88 2.12 0.88h16c0.828 0 1.579-0.34 2.12-0.88s0.88-1.292 0.88-2.12v-11.988c0-0.010 0-0.020 0-0.030-0.005-0.821-0.343-1.565-0.88-2.102-0.541-0.54-1.292-0.88-2.12-0.88h-16c-0.828 0-1.579 0.34-2.12 0.88-0.537 0.537-0.875 1.281-0.88 2.103zM20.894 5.554l-8.894 6.225-8.894-6.225c0.048-0.096 0.112-0.183 0.188-0.259 0.185-0.185 0.434-0.295 0.706-0.295h16c0.272 0 0.521 0.11 0.705 0.295 0.076 0.076 0.14 0.164 0.188 0.259z"></path>
<path d="M28 5h-24c-2.209 0-4 1.792-4 4v13c0 2.209 1.791 4 4 4h24c2.209 0 4-1.791 4-4v-13c0-2.208-1.791-4-4-4zM2 10.25l6.999 5.25-6.999 5.25v-10.5zM30 22c0 1.104-0.898 2-2 2h-24c-1.103 0-2-0.896-2-2l7.832-5.875 4.368 3.277c0.533 0.398 1.166 0.6 1.8 0.6 0.633 0 1.266-0.201 1.799-0.6l4.369-3.277 7.832 5.875zM30 20.75l-7-5.25 7-5.25v10.5zM17.199 18.602c-0.349 0.262-0.763 0.4-1.199 0.4s-0.851-0.139-1.2-0.4l-12.8-9.602c0-1.103 0.897-2 2-2h24c1.102 0 2 0.897 2 2l-12.801 9.602z"></path>
</symbol> </symbol>
<symbol <symbol
id="icon-mouse" id="icon-mouse"
@ -95,15 +142,12 @@ export class Symbols extends Component<any, any> {
</g> </g>
</symbol> </symbol>
<symbol id="icon-search" viewBox="0 0 32 32"> <symbol id="icon-search" viewBox="0 0 32 32">
<title>search</title>
<path d="M31.008 27.231l-7.58-6.447c-0.784-0.705-1.622-1.029-2.299-0.998 1.789-2.096 2.87-4.815 2.87-7.787 0-6.627-5.373-12-12-12s-12 5.373-12 12 5.373 12 12 12c2.972 0 5.691-1.081 7.787-2.87-0.031 0.677 0.293 1.515 0.998 2.299l6.447 7.58c1.104 1.226 2.907 1.33 4.007 0.23s0.997-2.903-0.23-4.007zM12 20c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8z"></path> <path d="M31.008 27.231l-7.58-6.447c-0.784-0.705-1.622-1.029-2.299-0.998 1.789-2.096 2.87-4.815 2.87-7.787 0-6.627-5.373-12-12-12s-12 5.373-12 12 5.373 12 12 12c2.972 0 5.691-1.081 7.787-2.87-0.031 0.677 0.293 1.515 0.998 2.299l6.447 7.58c1.104 1.226 2.907 1.33 4.007 0.23s0.997-2.903-0.23-4.007zM12 20c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8z"></path>
</symbol> </symbol>
<symbol id="icon-github" viewBox="0 0 32 32"> <symbol id="icon-github" viewBox="0 0 32 32">
<title>github</title>
<path d="M16 0.395c-8.836 0-16 7.163-16 16 0 7.069 4.585 13.067 10.942 15.182 0.8 0.148 1.094-0.347 1.094-0.77 0-0.381-0.015-1.642-0.022-2.979-4.452 0.968-5.391-1.888-5.391-1.888-0.728-1.849-1.776-2.341-1.776-2.341-1.452-0.993 0.11-0.973 0.11-0.973 1.606 0.113 2.452 1.649 2.452 1.649 1.427 2.446 3.743 1.739 4.656 1.33 0.143-1.034 0.558-1.74 1.016-2.14-3.554-0.404-7.29-1.777-7.29-7.907 0-1.747 0.625-3.174 1.649-4.295-0.166-0.403-0.714-2.030 0.155-4.234 0 0 1.344-0.43 4.401 1.64 1.276-0.355 2.645-0.532 4.005-0.539 1.359 0.006 2.729 0.184 4.008 0.539 3.054-2.070 4.395-1.64 4.395-1.64 0.871 2.204 0.323 3.831 0.157 4.234 1.026 1.12 1.647 2.548 1.647 4.295 0 6.145-3.743 7.498-7.306 7.895 0.574 0.497 1.085 1.47 1.085 2.963 0 2.141-0.019 3.864-0.019 4.391 0 0.426 0.288 0.925 1.099 0.768 6.354-2.118 10.933-8.113 10.933-15.18 0-8.837-7.164-16-16-16z"></path> <path d="M16 0.395c-8.836 0-16 7.163-16 16 0 7.069 4.585 13.067 10.942 15.182 0.8 0.148 1.094-0.347 1.094-0.77 0-0.381-0.015-1.642-0.022-2.979-4.452 0.968-5.391-1.888-5.391-1.888-0.728-1.849-1.776-2.341-1.776-2.341-1.452-0.993 0.11-0.973 0.11-0.973 1.606 0.113 2.452 1.649 2.452 1.649 1.427 2.446 3.743 1.739 4.656 1.33 0.143-1.034 0.558-1.74 1.016-2.14-3.554-0.404-7.29-1.777-7.29-7.907 0-1.747 0.625-3.174 1.649-4.295-0.166-0.403-0.714-2.030 0.155-4.234 0 0 1.344-0.43 4.401 1.64 1.276-0.355 2.645-0.532 4.005-0.539 1.359 0.006 2.729 0.184 4.008 0.539 3.054-2.070 4.395-1.64 4.395-1.64 0.871 2.204 0.323 3.831 0.157 4.234 1.026 1.12 1.647 2.548 1.647 4.295 0 6.145-3.743 7.498-7.306 7.895 0.574 0.497 1.085 1.47 1.085 2.963 0 2.141-0.019 3.864-0.019 4.391 0 0.426 0.288 0.925 1.099 0.768 6.354-2.118 10.933-8.113 10.933-15.18 0-8.837-7.164-16-16-16z"></path>
</symbol> </symbol>
<symbol id="icon-spinner" viewBox="0 0 32 32"> <symbol id="icon-spinner" viewBox="0 0 32 32">
<title>spinner</title>
<path d="M16 32c-4.274 0-8.292-1.664-11.314-4.686s-4.686-7.040-4.686-11.314c0-3.026 0.849-5.973 2.456-8.522 1.563-2.478 3.771-4.48 6.386-5.791l1.344 2.682c-2.126 1.065-3.922 2.693-5.192 4.708-1.305 2.069-1.994 4.462-1.994 6.922 0 7.168 5.832 13 13 13s13-5.832 13-13c0-2.459-0.69-4.853-1.994-6.922-1.271-2.015-3.066-3.643-5.192-4.708l1.344-2.682c2.615 1.31 4.824 3.313 6.386 5.791 1.607 2.549 2.456 5.495 2.456 8.522 0 4.274-1.664 8.292-4.686 11.314s-7.040 4.686-11.314 4.686z"></path> <path d="M16 32c-4.274 0-8.292-1.664-11.314-4.686s-4.686-7.040-4.686-11.314c0-3.026 0.849-5.973 2.456-8.522 1.563-2.478 3.771-4.48 6.386-5.791l1.344 2.682c-2.126 1.065-3.922 2.693-5.192 4.708-1.305 2.069-1.994 4.462-1.994 6.922 0 7.168 5.832 13 13 13s13-5.832 13-13c0-2.459-0.69-4.853-1.994-6.922-1.271-2.015-3.066-3.643-5.192-4.708l1.344-2.682c2.615 1.31 4.824 3.313 6.386 5.791 1.607 2.549 2.456 5.495 2.456 8.522 0 4.274-1.664 8.292-4.686 11.314s-7.040 4.686-11.314 4.686z"></path>
</symbol> </symbol>
</defs> </defs>

View file

@ -267,6 +267,7 @@ export class User extends Component<any, UserState> {
SortType[this.state.sort] SortType[this.state.sort]
}`} }`}
target="_blank" target="_blank"
title="RSS"
> >
<svg class="icon mx-2 text-muted small"> <svg class="icon mx-2 text-muted small">
<use xlinkHref="#icon-rss">#</use> <use xlinkHref="#icon-rss">#</use>

1
ui/src/index.html vendored
View file

@ -14,6 +14,7 @@
<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/toastify.css" />
<link rel="stylesheet" type="text/css" href="/static/assets/css/selectr.min.css" /> <link rel="stylesheet" type="text/css" href="/static/assets/css/selectr.min.css" />
<link rel="stylesheet" type="text/css" href="/static/assets/css/tippy.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

@ -208,6 +208,7 @@ export interface Comment {
saved?: boolean; saved?: boolean;
user_mention_id?: number; // For mention type user_mention_id?: number; // For mention type
recipient_id?: number; recipient_id?: number;
depth?: number;
} }
export interface Category { export interface Category {

View file

@ -61,6 +61,7 @@ export class WebSocketService {
private constructor() { private constructor() {
this.ws = new ReconnectingWebSocket(wsUri); this.ws = new ReconnectingWebSocket(wsUri);
let firstConnect = true;
this.subject = Observable.create((obs: any) => { this.subject = Observable.create((obs: any) => {
this.ws.onmessage = e => { this.ws.onmessage = e => {
@ -68,13 +69,19 @@ export class WebSocketService {
}; };
this.ws.onopen = () => { this.ws.onopen = () => {
console.log(`Connected to ${wsUri}`); console.log(`Connected to ${wsUri}`);
if (UserService.Instance.user) { if (UserService.Instance.user) {
this.userJoin(); this.userJoin();
} }
let res: WebSocketJsonResponse = {
reconnect: true, if (!firstConnect) {
}; let res: WebSocketJsonResponse = {
obs.next(res); reconnect: true,
};
obs.next(res);
}
firstConnect = false;
}; };
}).pipe(share()); }).pipe(share());
} }

45
ui/src/utils.ts vendored
View file

@ -41,6 +41,7 @@ import markdown_it_container from 'markdown-it-container';
import twemoji from 'twemoji'; import twemoji from 'twemoji';
import emojiShortName from 'emoji-short-name'; import emojiShortName from 'emoji-short-name';
import Toastify from 'toastify-js'; import Toastify from 'toastify-js';
import tippy from 'tippy.js';
export const repoUrl = 'https://github.com/dessalines/lemmy'; export const repoUrl = 'https://github.com/dessalines/lemmy';
export const markdownHelpUrl = '/docs/about_guide.html'; export const markdownHelpUrl = '/docs/about_guide.html';
@ -445,6 +446,7 @@ export function setupTribute(): Tribute {
allowSpaces: false, allowSpaces: false,
autocompleteMode: true, autocompleteMode: true,
menuItemLimit: mentionDropdownFetchLimit, menuItemLimit: mentionDropdownFetchLimit,
menuShowMinLength: 2,
}, },
// Users // Users
{ {
@ -458,6 +460,7 @@ export function setupTribute(): Tribute {
allowSpaces: false, allowSpaces: false,
autocompleteMode: true, autocompleteMode: true,
menuItemLimit: mentionDropdownFetchLimit, menuItemLimit: mentionDropdownFetchLimit,
menuShowMinLength: 2,
}, },
// Communities // Communities
@ -472,11 +475,23 @@ export function setupTribute(): Tribute {
allowSpaces: false, allowSpaces: false,
autocompleteMode: true, autocompleteMode: true,
menuItemLimit: mentionDropdownFetchLimit, menuItemLimit: mentionDropdownFetchLimit,
menuShowMinLength: 2,
}, },
], ],
}); });
} }
let tippyInstance = tippy('[data-tippy-content]');
export function setupTippy() {
tippyInstance.forEach(e => e.destroy());
tippyInstance = tippy('[data-tippy-content]', {
delay: [500, 0],
// Display on "long press"
touch: ['hold', 500],
});
}
function userSearch(text: string, cb: any) { function userSearch(text: string, cb: any) {
if (text) { if (text) {
let form: SearchForm = { let form: SearchForm = {
@ -714,7 +729,11 @@ function convertCommentSortType(sort: SortType): CommentSortType {
} }
} }
export function postSort(posts: Array<Post>, sort: SortType) { export function postSort(
posts: Array<Post>,
sort: SortType,
communityType: boolean
) {
// First, put removed and deleted comments at the bottom, then do your other sorts // First, put removed and deleted comments at the bottom, then do your other sorts
if ( if (
sort == SortType.TopAll || sort == SortType.TopAll ||
@ -725,13 +744,17 @@ export function postSort(posts: Array<Post>, sort: SortType) {
) { ) {
posts.sort( posts.sort(
(a, b) => (a, b) =>
+a.removed - +b.removed || +a.deleted - +b.deleted || b.score - a.score +a.removed - +b.removed ||
+a.deleted - +b.deleted ||
(communityType && +b.stickied - +a.stickied) ||
b.score - a.score
); );
} else if (sort == SortType.New) { } else if (sort == SortType.New) {
posts.sort( posts.sort(
(a, b) => (a, b) =>
+a.removed - +b.removed || +a.removed - +b.removed ||
+a.deleted - +b.deleted || +a.deleted - +b.deleted ||
(communityType && +b.stickied - +a.stickied) ||
b.published.localeCompare(a.published) b.published.localeCompare(a.published)
); );
} else if (sort == SortType.Hot) { } else if (sort == SortType.Hot) {
@ -739,7 +762,25 @@ export function postSort(posts: Array<Post>, sort: SortType) {
(a, b) => (a, b) =>
+a.removed - +b.removed || +a.removed - +b.removed ||
+a.deleted - +b.deleted || +a.deleted - +b.deleted ||
(communityType && +b.stickied - +a.stickied) ||
hotRankPost(b) - hotRankPost(a) hotRankPost(b) - hotRankPost(a)
); );
} }
} }
export const colorList: Array<string> = [
hsl(0),
hsl(100),
hsl(150),
hsl(200),
hsl(250),
hsl(300),
];
function hsl(num: number) {
return `hsla(${num}, 35%, 50%, 1)`;
}
function randomHsl() {
return `hsla(${Math.random() * 360}, 100%, 50%, 1)`;
}

View file

@ -4,14 +4,16 @@
"no_posts": "No Posts.", "no_posts": "No Posts.",
"create_a_post": "Create a post", "create_a_post": "Create a post",
"create_post": "Create Post", "create_post": "Create Post",
"number_of_posts": "{{count}} Posts", "number_of_posts": "{{count}} Post",
"number_of_posts_plural": "{{count}} Posts",
"posts": "Posts", "posts": "Posts",
"related_posts": "These posts might be related", "related_posts": "These posts might be related",
"cross_posts": "This link has also been posted to:", "cross_posts": "This link has also been posted to:",
"cross_post": "cross-post", "cross_post": "cross-post",
"cross_posted_to": "cross-posted to: ", "cross_posted_to": "cross-posted to: ",
"comments": "Comments", "comments": "Comments",
"number_of_comments": "{{count}} Comments", "number_of_comments": "{{count}} Comment",
"number_of_comments_plural": "{{count}} Comments",
"remove_comment": "Remove Comment", "remove_comment": "Remove Comment",
"communities": "Communities", "communities": "Communities",
"users": "Users", "users": "Users",
@ -21,7 +23,8 @@
"subscribed_to_communities": "Subscribed to <1>communities</1>", "subscribed_to_communities": "Subscribed to <1>communities</1>",
"trending_communities": "Trending <1>communities</1>", "trending_communities": "Trending <1>communities</1>",
"list_of_communities": "List of communities", "list_of_communities": "List of communities",
"number_of_communities": "{{count}} Communities", "number_of_communities": "{{count}} Community",
"number_of_communities_plural": "{{count}} Communities",
"community_reqs": "lowercase, underscores, and no spaces.", "community_reqs": "lowercase, underscores, and no spaces.",
"create_private_message": "Create Private Message", "create_private_message": "Create Private Message",
"send_secure_message": "Send Secure Message", "send_secure_message": "Send Secure Message",
@ -79,10 +82,14 @@
"creator": "creator", "creator": "creator",
"username": "Username", "username": "Username",
"email_or_username": "Email or Username", "email_or_username": "Email or Username",
"number_of_users": "{{count}} Users", "number_of_users": "{{count}} User",
"number_of_subscribers": "{{count}} Subscribers", "number_of_users_plural": "{{count}} Users",
"number_of_points": "{{count}} Points", "number_of_subscribers": "{{count}} Subscriber",
"number_online": "{{count}} Users Online", "number_of_subscribers_plural": "{{count}} Subscribers",
"number_of_points": "{{count}} Point",
"number_of_points_plural": "{{count}} Points",
"number_online": "{{count}} User Online",
"number_online_plural": "{{count}} Users Online",
"name": "Name", "name": "Name",
"title": "Title", "title": "Title",
"category": "Category", "category": "Category",
@ -146,6 +153,8 @@
"browser_default": "Browser Default", "browser_default": "Browser Default",
"downvotes_disabled": "Downvotes disabled", "downvotes_disabled": "Downvotes disabled",
"enable_downvotes": "Enable Downvotes", "enable_downvotes": "Enable Downvotes",
"upvote": "Upvote",
"downvote": "Downvote",
"open_registration": "Open Registration", "open_registration": "Open Registration",
"registration_closed": "Registration closed", "registration_closed": "Registration closed",
"enable_nsfw": "Enable NSFW", "enable_nsfw": "Enable NSFW",

20
ui/yarn.lock vendored
View file

@ -104,6 +104,11 @@
lodash "^4.17.13" lodash "^4.17.13"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@popperjs/core@^2.0.6":
version "2.0.6"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.0.6.tgz#5a39ac118811ca844155b0ad5190b8c24f35e533"
integrity sha512-zj7Gw8QC4jmR92eKUvtrZUEpl2ypRbq+qlE4pwf9n2hnUO9BOAcWUs4/Ht+gNIbFt98xtqhLvccdCfD469MzpQ==
"@samverschueren/stream-to-observable@^0.3.0": "@samverschueren/stream-to-observable@^0.3.0":
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"
@ -4432,6 +4437,13 @@ tiny-warning@^1.0.0:
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
tippy.js@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.0.0.tgz#6bc4f477ea82ef344db51ae34f106a8b25f2d02c"
integrity sha512-2NVc5A8cnO9N/Fk+tTU6KEm6m8ZXT1u3pOY756zZ5BE38rWDl/hVyoWhTfM79HW1nEJSpn/VujqAMMZGHsE9qQ==
dependencies:
"@popperjs/core" "^2.0.6"
tmp@^0.0.33: tmp@^0.0.33:
version "0.0.33" version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@ -4494,10 +4506,10 @@ tough-cookie@~2.4.3:
psl "^1.1.24" psl "^1.1.24"
punycode "^1.4.1" punycode "^1.4.1"
tributejs@^4.1.1: tributejs@^5.0.0:
version "4.1.1" version "5.0.0"
resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-4.1.1.tgz#f169a4ad12e485241140ec1ab987b460950c974c" resolved "https://registry.yarnpkg.com/tributejs/-/tributejs-5.0.0.tgz#2c5301a79c19d7a72d23e995bf7c9f47c2a34f23"
integrity sha512-jc+PcaiNzMjCn2LAQb3i4ic94EsSfLW8Jlk1sK2cb6hLcZFalU9ThcF8rxuKkTUKv1GIvTwN8XseLzCXLxB4lw== integrity sha512-aPUpq4+NTXRq1OcdoeiFg9d+wM+J0b7dpL7MNVxqo8JIgChtkx8HnsPVl/uZ4Z1ChTF9UI1ffbvTfJRHqJjjAw==
ts-node@^8.6.2: ts-node@^8.6.2:
version "8.6.2" version "8.6.2"