A first pass at adding icons, and tippy tooltips.

- Adding icons for post-listing, comment-node, and navbar.
- Adding html titles.
- Updating moment expand to use users locale.
This commit is contained in:
Dessalines 2020-03-03 02:29:45 -05:00
parent 2a3b866577
commit 65779be906
19 changed files with 373 additions and 109 deletions

View file

@ -95,8 +95,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;
@ -225,3 +234,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}

1
ui/package.json vendored
View file

@ -41,6 +41,7 @@
"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": "^4.1.1",
"twemoji": "^12.1.2", "twemoji": "^12.1.2",

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,7 @@ import {
isMod, isMod,
pictshareAvatarThumbnail, pictshareAvatarThumbnail,
showAvatars, showAvatars,
setupTippy,
} from '../utils'; } from '../utils';
import moment from 'moment'; import moment from 'moment';
import { MomentTime } from './moment-time'; import { MomentTime } from './moment-time';
@ -102,6 +103,10 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this.handleCommentDownvote = this.handleCommentDownvote.bind(this); this.handleCommentDownvote = this.handleCommentDownvote.bind(this);
} }
componentDidUpdate() {
setupTippy();
}
componentWillReceiveProps(nextProps: CommentNodeProps) { componentWillReceiveProps(nextProps: CommentNodeProps) {
this.state.my_vote = nextProps.node.comment.my_vote; this.state.my_vote = nextProps.node.comment.my_vote;
this.state.upvotes = nextProps.node.comment.upvotes; this.state.upvotes = nextProps.node.comment.upvotes;
@ -128,18 +133,22 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this.state.my_vote == 1 ? 'text-info' : 'text-muted' this.state.my_vote == 1 ? 'text-info' : 'text-muted'
}`} }`}
onClick={linkEvent(node, this.handleCommentUpvote)} onClick={linkEvent(node, this.handleCommentUpvote)}
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`}>{this.state.score}</div> <div class={`unselectable font-weight-bold text-muted`}>
{this.state.score}
</div>
{WebSocketService.Instance.site.enable_downvotes && ( {WebSocketService.Instance.site.enable_downvotes && (
<button <button
className={`vote-animate btn btn-link p-0 ${ className={`vote-animate btn btn-link p-0 ${
this.state.my_vote == -1 ? 'text-danger' : 'text-muted' this.state.my_vote == -1 ? 'text-danger' : 'text-muted'
}`} }`}
onClick={linkEvent(node, this.handleCommentDownvote)} onClick={linkEvent(node, this.handleCommentDownvote)}
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>
@ -192,11 +201,19 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
</li> </li>
)} )}
<li className="list-inline-item"> <li className="list-inline-item">
<span> <span className="text-info">
(<span className="text-info">+{this.state.upvotes}</span> <svg class="small icon icon-inline mr-1">
<span> | </span> <use xlinkHref="#icon-arrow-up"></use>
<span className="text-danger">-{this.state.downvotes}</span> </svg>
<span>) </span> {this.state.upvotes}
</span>
</li>
<li className="list-inline-item">
<span className="text-danger">
<svg class="small icon icon-inline mr-1">
<use xlinkHref="#icon-arrow-down"></use>
</svg>
{this.state.downvotes}
</span> </span>
</li> </li>
{this.props.showCommunity && ( {this.props.showCommunity && (
@ -214,7 +231,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
</li> </li>
<li className="list-inline-item"> <li className="list-inline-item">
<div <div
className="pointer text-monospace" className="unselectable pointer text-monospace"
onClick={linkEvent(this, this.handleCommentCollapse)} onClick={linkEvent(this, this.handleCommentCollapse)}
> >
{this.state.collapsed ? '[+]' : '[-]'} {this.state.collapsed ? '[+]' : '[-]'}
@ -239,97 +256,141 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
dangerouslySetInnerHTML={mdToHtml(this.commentUnlessRemoved)} dangerouslySetInnerHTML={mdToHtml(this.commentUnlessRemoved)}
/> />
)} )}
<ul class="list-inline mb-1 text-muted small font-weight-bold"> <ul class="list-inline mb-1 text-muted font-weight-bold h6">
{this.props.markable && ( {this.props.markable && (
<li className="list-inline-item"> <li className="list-inline-item-action">
<span <span
class="pointer" class="pointer"
onClick={linkEvent(this, this.handleMarkRead)} onClick={linkEvent(this, this.handleMarkRead)}
> data-tippy-content={
{node.comment.read node.comment.read
? i18n.t('mark_as_unread') ? i18n.t('mark_as_unread')
: i18n.t('mark_as_read')} : i18n.t('mark_as_read')
}
>
<svg
class={`icon icon-inline ${node.comment.read &&
'text-success'}`}
>
<use xlinkHref="#icon-check"></use>
</svg>
</span> </span>
</li> </li>
)} )}
{UserService.Instance.user && !this.props.viewOnly && ( {UserService.Instance.user && !this.props.viewOnly && (
<> <>
<li className="list-inline-item"> <li className="list-inline-item-action">
<span <span
class="pointer" class="pointer"
onClick={linkEvent(this, this.handleReplyClick)} onClick={linkEvent(this, this.handleReplyClick)}
data-tippy-content={i18n.t('reply')}
> >
{i18n.t('reply')} <svg class="icon icon-inline">
<use xlinkHref="#icon-reply1"></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.handleSaveCommentClick)} onClick={linkEvent(this, this.handleSaveCommentClick)}
data-tippy-content={
node.comment.saved ? i18n.t('unsave') : i18n.t('save')
}
> >
{node.comment.saved ? i18n.t('unsave') : i18n.t('save')} <svg
class={`icon icon-inline ${node.comment.saved &&
'text-warning'}`}
>
<use xlinkHref="#icon-star"></use>
</svg>
</span> </span>
</li> </li>
{!this.myComment && ( {!this.myComment && (
<li className="list-inline-item"> <li className="list-inline-item-action">
<Link <Link
class="text-muted" class="text-muted"
to={`/create_private_message?recipient_id=${node.comment.creator_id}`} to={`/create_private_message?recipient_id=${node.comment.creator_id}`}
title={i18n.t('message').toLowerCase()}
> >
{i18n.t('message').toLowerCase()} <svg class="icon icon-inline">
<use xlinkHref="#icon-mail"></use>
</svg>
</Link> </Link>
</li> </li>
)} )}
<li className="list-inline-item"> <li className="list-inline-item-action">
<Link <Link
className="text-muted" className="text-muted"
to={`/post/${node.comment.post_id}/comment/${node.comment.id}`} to={`/post/${node.comment.post_id}/comment/${node.comment.id}`}
title={i18n.t('link')}
> >
{i18n.t('link')} <svg class="icon icon-inline">
<use xlinkHref="#icon-external-link"></use>
</svg>
</Link> </Link>
</li> </li>
{!this.state.showAdvanced ? ( {!this.state.showAdvanced ? (
<li className="list-inline-item"> <li className="list-inline-item-action">
<span <span
className="pointer" className="unselectable 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>
) : ( ) : (
<> <>
<li className="list-inline-item"></li> <li className="list-inline-item-action">
<li className="list-inline-item">
<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-eye"></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"></li>
<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"> <li className="list-inline-item-action">
<span <span
class="pointer" class="pointer"
onClick={linkEvent( onClick={linkEvent(
this, this,
this.handleDeleteClick this.handleDeleteClick
)} )}
> data-tippy-content={
{!node.comment.deleted !node.comment.deleted
? i18n.t('delete') ? i18n.t('delete')
: i18n.t('restore')} : i18n.t('restore')
}
>
<svg
class={`icon icon-inline ${node.comment
.deleted && 'text-danger'}`}
>
<use xlinkHref="#icon-trash"></use>
</svg>
</span> </span>
</li> </li>
</> </>
@ -337,7 +398,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
{/* Admins and mods can remove comments */} {/* Admins and mods can remove comments */}
{(this.canMod || this.canAdmin) && ( {(this.canMod || this.canAdmin) && (
<> <>
<li className="list-inline-item"> <li className="list-inline-item-action">
{!node.comment.removed ? ( {!node.comment.removed ? (
<span <span
class="pointer" class="pointer"
@ -366,7 +427,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
{this.canMod && ( {this.canMod && (
<> <>
{!this.isMod && ( {!this.isMod && (
<li className="list-inline-item"> <li className="list-inline-item-action">
{!node.comment.banned_from_community ? ( {!node.comment.banned_from_community ? (
<span <span
class="pointer" class="pointer"
@ -391,7 +452,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
</li> </li>
)} )}
{!node.comment.banned_from_community && ( {!node.comment.banned_from_community && (
<li className="list-inline-item"> <li className="list-inline-item-action">
{!this.state.showConfirmAppointAsMod ? ( {!this.state.showConfirmAppointAsMod ? (
<span <span
class="pointer" class="pointer"
@ -436,7 +497,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
{/* Community creators and admins can transfer community to another mod */} {/* Community creators and admins can transfer community to another mod */}
{(this.amCommunityCreator || this.canAdmin) && {(this.amCommunityCreator || this.canAdmin) &&
this.isMod && ( this.isMod && (
<li className="list-inline-item"> <li className="list-inline-item-action">
{!this.state.showConfirmTransferCommunity ? ( {!this.state.showConfirmTransferCommunity ? (
<span <span
class="pointer" class="pointer"
@ -479,7 +540,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
{this.canAdmin && ( {this.canAdmin && (
<> <>
{!this.isAdmin && ( {!this.isAdmin && (
<li className="list-inline-item"> <li className="list-inline-item-action">
{!node.comment.banned ? ( {!node.comment.banned ? (
<span <span
class="pointer" class="pointer"
@ -504,7 +565,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
</li> </li>
)} )}
{!node.comment.banned && ( {!node.comment.banned && (
<li className="list-inline-item"> <li className="list-inline-item-action">
{!this.state.showConfirmAppointAsAdmin ? ( {!this.state.showConfirmAppointAsAdmin ? (
<span <span
class="pointer" class="pointer"
@ -548,7 +609,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
)} )}
{/* Site Creator can transfer to another admin */} {/* Site Creator can transfer to another admin */}
{this.amSiteCreator && this.isAdmin && ( {this.amSiteCreator && this.isAdmin && (
<li className="list-inline-item"> <li className="list-inline-item-action">
{!this.state.showConfirmTransferSite ? ( {!this.state.showConfirmTransferSite ? (
<span <span
class="pointer" class="pointer"

View file

@ -208,6 +208,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>

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

@ -441,6 +441,7 @@ 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 mx-1 text-muted small">
<use xlinkHref="#icon-rss">#</use> <use xlinkHref="#icon-rss">#</use>
@ -454,6 +455,7 @@ 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 mx-1 text-muted small">
<use xlinkHref="#icon-rss">#</use> <use xlinkHref="#icon-rss">#</use>

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, setupTippy } from '../utils';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
interface MomentTimeProps { interface MomentTimeProps {
@ -20,16 +20,37 @@ export class MomentTime extends Component<MomentTimeProps, any> {
moment.locale(lang); moment.locale(lang);
} }
componentDidMount() {
setupTippy();
}
render() { render() {
if (this.props.data.updated) { if (this.props.data.updated) {
return ( return (
<span title={this.props.data.updated} className="font-italics"> <span
data-tippy-content={this.format(this.props.data.updated)}
className="font-italics pointer unselectable"
>
{i18n.t('modified')} {moment.utc(this.props.data.updated).fromNow()} {i18n.t('modified')} {moment.utc(this.props.data.updated).fromNow()}
</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()}
</span>
);
} }
} }
format(input: string): string {
return moment
.utc(input)
.local()
.format('LLLL');
}
} }

View file

@ -26,6 +26,7 @@ import {
fetchLimit, fetchLimit,
isCommentType, isCommentType,
toast, toast,
setupTippy,
} from '../utils'; } from '../utils';
import { version } from '../version'; import { version } from '../version';
import { i18n } from '../i18next'; import { i18n } from '../i18next';
@ -84,6 +85,10 @@ export class Navbar extends Component<any, NavbarState> {
WebSocketService.Instance.getSite(); WebSocketService.Instance.getSite();
} }
componentDidMount() {
setupTippy();
}
render() { render() {
return this.navbar(); return this.navbar();
} }
@ -105,6 +110,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 +119,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 +139,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 +169,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 +184,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 +203,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>
)} )}

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';
@ -101,6 +102,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
} }
} }
componentDidUpdate() {
setupTippy();
}
componentWillReceiveProps(nextProps: PostListingProps) { componentWillReceiveProps(nextProps: PostListingProps) {
this.state.my_vote = nextProps.post.my_vote; this.state.my_vote = nextProps.post.my_vote;
this.state.upvotes = nextProps.post.upvotes; this.state.upvotes = nextProps.post.upvotes;
@ -185,7 +190,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 +251,13 @@ 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 font-weight-bold text-muted px-1`}>
{this.state.score} {this.state.score}
</div> </div>
{WebSocketService.Instance.site.enable_downvotes && ( {WebSocketService.Instance.site.enable_downvotes && (
@ -260,6 +266,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>
@ -333,8 +340,8 @@ 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)}
> >
[+] [+]
@ -342,7 +349,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
) : ( ) : (
<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)}
> >
[-] [-]
@ -439,22 +446,37 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
</span> </span>
</li> </li>
<li className="list-inline-item"> <li className="list-inline-item">
<span> <span className="text-info">
(<span className="text-info">+{this.state.upvotes}</span> <svg class="small icon icon-inline mr-1">
<span> | </span> <use xlinkHref="#icon-arrow-up"></use>
<span className="text-danger">-{this.state.downvotes}</span> </svg>
<span>) </span> {this.state.upvotes}
</span> </span>
</li> </li>
<li className="list-inline-item"> <li className="list-inline-item">
<Link className="text-muted" to={`/post/${post.id}`}> <span className="text-danger">
{i18n.t('number_of_comments', { <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">
<Link
className="text-muted"
title={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 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,65 +492,90 @@ 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 h6 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 !post.deleted
? i18n.t('delete') ? i18n.t('delete')
: i18n.t('restore')} : i18n.t('restore')
}
>
<svg
class={`icon icon-inline ${post.deleted &&
'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)}
@ -539,24 +586,40 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
)} )}
{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 post.locked
? i18n.t('unlock') ? i18n.t('unlock')
: i18n.t('lock')} : i18n.t('lock')
}
>
<svg
class={`icon icon-inline ${post.locked &&
'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 post.stickied
? i18n.t('unsticky') ? i18n.t('unsticky')
: i18n.t('sticky')} : i18n.t('sticky')
}
>
<svg
class={`icon icon-inline ${post.stickied &&
'text-success'}`}
>
<use xlinkHref="#icon-pin"></use>
</svg>
</span> </span>
</li> </li>
</> </>

View file

@ -15,40 +15,66 @@ 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-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-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-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="M15 17v-2.99c0-0.003 0-0.006 0-0.010 0-2.209-1.791-4-4-4 0 0-0 0-0 0h-3v5l-6-6 6-6v5h3c3.314 0 6 2.686 6 6v0 3h-2z"></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 +121,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" />

12
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';
@ -477,6 +478,17 @@ export function setupTribute(): Tribute {
}); });
} }
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 = {

View file

@ -146,6 +146,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",

12
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"