import { SiteForm } from './site-form';
import { UserListing } from './user-listing';
import { HtmlTags } from './html-tags';
+import { Spinner } from './icon';
import { i18n } from '../i18next';
import { InitialFetchRequest } from 'shared/interfaces';
/>
{this.state.loading ? (
<h5>
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
</h5>
) : (
<div class="row">
<div class="col-12">
<button type="submit" class="btn btn-secondary mr-2">
{this.state.siteConfigLoading ? (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
) : (
capitalizeFirstLetter(i18n.t('save'))
)}
import { Component } from 'inferno';
import { i18n } from '../i18next';
+import { Icon } from './icon';
interface CakeDayProps {
creatorName: string;
className={`mx-2 d-inline-block unselectable pointer`}
data-tippy-content={this.cakeDayTippy()}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-cake"></use>
- </svg>
+ <Icon icon="cake" classes="icon-inline" />
</div>
);
}
import { i18n } from '../i18next';
import { T } from 'inferno-i18next';
import { MarkdownTextArea } from './markdown-textarea';
+import { Icon } from './icon';
interface CommentFormProps {
postId?: number;
/>
) : (
<div class="alert alert-light" role="alert">
- <svg class="icon icon-inline mr-2">
- <use xlinkHref="#icon-alert-triangle"></use>
- </svg>
+ <Icon icon="alert-triangle" classes="icon-inline mr-2" />
<T i18nKey="must_login" class="d-inline">
#
<Link className="alert-link" to="/login">
import { CommentNodes } from './comment-nodes';
import { UserListing } from './user-listing';
import { CommunityLink } from './community-link';
+import { Icon, Spinner } from './icon';
import { i18n } from '../i18next';
interface CommentNodeState {
{this.state.readLoading ? (
this.loadingIcon
) : (
- <svg
- class={`icon icon-inline ${
+ <Icon
+ icon="check"
+ classes={`icon-inline ${
this.commentOrMentionRead && 'text-success'
}`}
- >
- <use xlinkHref="#icon-check"></use>
- </svg>
+ />
)}
</button>
)}
data-tippy-content={i18n.t('upvote')}
aria-label={i18n.t('upvote')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-arrow-up1"></use>
- </svg>
+ <Icon icon="arrow-up1" classes="icon-inline" />
{this.state.upvotes !== this.state.score && (
<span class="ml-1">{this.state.upvotes}</span>
)}
data-tippy-content={i18n.t('downvote')}
aria-label={i18n.t('downvote')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-arrow-down1"></use>
- </svg>
+ <Icon icon="arrow-down1" classes="icon-inline" />
{this.state.upvotes !== this.state.score && (
<span class="ml-1">{this.state.downvotes}</span>
)}
data-tippy-content={i18n.t('reply')}
aria-label={i18n.t('reply')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-reply1"></use>
- </svg>
+ <Icon icon="reply1" classes="icon-inline" />
</button>
{!this.state.showAdvanced ? (
<button
data-tippy-content={i18n.t('more')}
aria-label={i18n.t('more')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-more-vertical"></use>
- </svg>
+ <Icon icon="more-vertical" classes="icon-inline" />
</button>
) : (
<>
to={`/create_private_message/recipient/${cv.creator.id}`}
title={i18n.t('message').toLowerCase()}
>
- <svg class="icon">
- <use xlinkHref="#icon-mail"></use>
- </svg>
+ <Icon icon="mail" />
</Link>
</button>
)}
{this.state.saveLoading ? (
this.loadingIcon
) : (
- <svg
- class={`icon icon-inline ${
+ <Icon
+ icon="star"
+ classes={`icon-inline ${
cv.saved && 'text-warning'
}`}
- >
- <use xlinkHref="#icon-star"></use>
- </svg>
+ />
)}
</button>
<button
data-tippy-content={i18n.t('view_source')}
aria-label={i18n.t('view_source')}
>
- <svg
- class={`icon icon-inline ${
+ <Icon
+ icon="file-text"
+ classes={`icon-inline ${
this.state.viewSource && 'text-success'
}`}
- >
- <use xlinkHref="#icon-file-text"></use>
- </svg>
+ />
</button>
{this.myComment && (
<>
data-tippy-content={i18n.t('edit')}
aria-label={i18n.t('edit')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-edit"></use>
- </svg>
+ <Icon icon="edit" classes="icon-inline" />
</button>
<button
class="btn btn-link btn-animate text-muted"
: i18n.t('restore')
}
>
- <svg
- class={`icon icon-inline ${
+ <Icon
+ icon="trash"
+ classes={`icon-inline ${
cv.comment.deleted && 'text-danger'
}`}
- >
- <use xlinkHref="#icon-trash"></use>
- </svg>
+ />
</button>
</>
)}
to={`/post/${cv.post.id}/comment/${cv.comment.id}`}
title={this.props.showContext ? i18n.t('show_context') : i18n.t('link')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-link"></use>
- </svg>
+ <Icon icon="link" classes="icon-inline" />
</Link>
);
}
get loadingIcon() {
- return (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
- );
+ return <Spinner />;
}
get myComment(): boolean {
setOptionalAuth,
} from '../utils';
import { CommunityLink } from './community-link';
+import { Spinner } from './icon';
import { i18n } from '../i18next';
import { InitialFetchRequest } from 'shared/interfaces';
path={this.context.router.route.match.url}
/>
{this.state.loading ? (
- <h5 class="">
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <h5>
+ <Spinner />
</h5>
) : (
<div>
import { MarkdownTextArea } from './markdown-textarea';
import { ImageUploadForm } from './image-upload-form';
+import { Icon, Spinner } from './icon';
interface CommunityFormProps {
community_view?: CommunityView; // If a community is given, that means this is an edit
class="pointer unselectable ml-2 text-muted"
data-tippy-content={i18n.t('name_explain')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-help-circle"></use>
- </svg>
+ <Icon icon="help-circle" classes="icon-inline" />
</span>
</label>
<div class="col-12">
class="pointer unselectable ml-2 text-muted"
data-tippy-content={i18n.t('display_name_explain')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-help-circle"></use>
- </svg>
+ <Icon icon="help-circle" classes="icon-inline" />
</span>
</label>
<div class="col-12">
disabled={this.state.loading}
>
{this.state.loading ? (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
) : this.props.community_view ? (
capitalizeFirstLetter(i18n.t('save'))
) : (
import { Sidebar } from './sidebar';
import { CommunityLink } from './community-link';
import { BannerIconHeader } from './banner-icon-header';
+import { Icon, Spinner } from './icon';
import {
wsJsonToRes,
fetchLimit,
<div class="container">
{this.state.communityLoading ? (
<h5>
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
</h5>
) : (
<div class="row">
return this.state.dataType == DataType.Post ? (
this.state.postsLoading ? (
<h5>
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
</h5>
) : (
<PostListings
)
) : this.state.commentsLoading ? (
<h5>
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
</h5>
) : (
<CommentNodes
title="RSS"
rel="noopener"
>
- <svg class="icon text-muted small">
- <use xlinkHref="#icon-rss">#</use>
- </svg>
+ <Icon icon="rss" classes="text-muted small" />
</a>
</div>
);
import { Subscription } from 'rxjs';
import { CommunityForm } from './community-form';
import { HtmlTags } from './html-tags';
+import { Spinner } from './icon';
import {
CommunityView,
UserOperation,
/>
{this.state.loading ? (
<h5>
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
</h5>
) : (
<div class="row">
import { Subscription } from 'rxjs';
import { PostForm } from './post-form';
import { HtmlTags } from './html-tags';
+import { Spinner } from './icon';
import {
authField,
isBrowser,
/>
{this.state.loading ? (
<h5>
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
</h5>
) : (
<div class="row">
import { Subscription } from 'rxjs';
import { PrivateMessageForm } from './private-message-form';
import { HtmlTags } from './html-tags';
+import { Spinner } from './icon';
import { UserService, WebSocketService } from '../services';
import {
SiteView,
/>
{this.state.loading ? (
<h5>
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
</h5>
) : (
<div class="row">
--- /dev/null
+import { Component } from 'inferno';
+
+interface IconProps {
+ icon: string;
+ classes?: string;
+}
+
+export class Icon extends Component<IconProps, any> {
+ constructor(props: any, context: any) {
+ super(props, context);
+ }
+
+ render() {
+ return (
+ <svg class={`icon ${this.props.classes}`}>
+ <title>{this.props.icon}</title>
+ <use xlinkHref={`#icon-${this.props.icon}`}></use>
+ </svg>
+ );
+ }
+}
+
+export class Spinner extends Component<any, any> {
+ constructor(props: any, context: any) {
+ super(props, context);
+ }
+
+ render() {
+ return <Icon icon="spinner" classes="icon-spinner spin" />;
+ }
+}
import { Post } from 'lemmy-js-client';
import { mdToHtml } from '../utils';
import { i18n } from '../i18next';
+import { Icon } from './icon';
interface FramelyCardProps {
post: Post;
rel="noopener"
>
{new URL(post.url).hostname}
- <svg class="ml-1 icon">
- <use xlinkHref="#icon-external-link"></use>
- </svg>
+ <Icon icon="external-link" classes="ml-1" />
</a>
</span>,
]}
import { UserService } from '../services';
import { toast, randomStr } from '../utils';
import { i18n } from '../i18next';
+import { Icon } from './icon';
interface ImageUploadFormProps {
uploadTitle: string;
onClick={linkEvent(this, this.handleRemoveImage)}
aria-label={i18n.t('remove')}
>
- <svg class="icon mini-overlay">
- <use xlinkHref="#icon-x"></use>
- </svg>
+ <Icon icon="x" classes="mini-overlay" />
</a>
</span>
)}
import { PrivateMessage } from './private-message';
import { HtmlTags } from './html-tags';
import { SortSelect } from './sort-select';
+import { Icon, Spinner } from './icon';
import { i18n } from '../i18next';
import { InitialFetchRequest } from 'shared/interfaces';
<div class="container">
{this.state.loading ? (
<h5>
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
</h5>
) : (
<div class="row">
title="RSS"
rel="noopener"
>
- <svg class="icon ml-2 text-muted small">
- <use xlinkHref="#icon-rss">#</use>
- </svg>
+ <Icon icon="rss" classes="ml-2 text-muted small" />
</a>
</small>
</h5>
} from '../utils';
import { i18n } from '../i18next';
import { HtmlTags } from './html-tags';
+import { Icon, Spinner } from './icon';
interface State {
loginForm: LoginForm;
<div class="form-group row">
<div class="col-sm-10">
<button type="submit" class="btn btn-secondary">
- {this.state.loginLoading ? (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
- ) : (
- i18n.t('login')
- )}
+ {this.state.loginLoading ? <Spinner /> : i18n.t('login')}
</button>
</div>
</div>
/>
{!validEmail(this.state.registerForm.email) && (
<div class="mt-2 mb-0 alert alert-light" role="alert">
- <svg class="icon icon-inline mr-2">
- <use xlinkHref="#icon-alert-triangle"></use>
- </svg>
+ <Icon icon="alert-triangle" classes="icon-inline mr-2" />
{i18n.t('no_password_reset')}
</div>
)}
class="btn btn-secondary"
onClick={linkEvent(this, this.handleRegenCaptcha)}
>
- <svg class="icon icon-refresh-cw">
- <use xlinkHref="#icon-refresh-cw"></use>
- </svg>
+ <Icon icon="refresh-cw" classes="icon-refresh-cw" />
</button>
</label>
{this.showCaptcha()}
<div class="form-group row">
<div class="col-sm-10">
<button type="submit" class="btn btn-secondary">
- {this.state.registerLoading ? (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
- ) : (
- i18n.t('sign_up')
- )}
+ {this.state.registerLoading ? <Spinner /> : i18n.t('sign_up')}
</button>
</div>
</div>
type="button"
disabled={this.state.captchaPlaying}
>
- <svg class="icon icon-play">
- <use xlinkHref="#icon-play"></use>
- </svg>
+ <Icon icon="play" classes="icon-play" />
</button>
)}
</>
import { UserListing } from './user-listing';
import { CommunityLink } from './community-link';
import { BannerIconHeader } from './banner-icon-header';
+import { Icon, Spinner } from './icon';
import {
wsJsonToRes,
mdToHtml,
aria-label={i18n.t('edit')}
data-tippy-content={i18n.t('edit')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-edit"></use>
- </svg>
+ <Icon icon="edit" classes="icon-inline" />
</span>
</li>
</ul>
<div class="main-content-wrapper">
{this.state.loading ? (
<h5>
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
</h5>
) : (
<div>
rel="noopener"
title="RSS"
>
- <svg class="icon text-muted small">
- <use xlinkHref="#icon-rss">#</use>
- </svg>
+ <Icon icon="rss" classes="text-muted small" />
</a>
)}
{this.state.listingType == ListingType.Local && (
rel="noopener"
title="RSS"
>
- <svg class="icon text-muted small">
- <use xlinkHref="#icon-rss">#</use>
- </svg>
+ <Icon icon="rss" classes="text-muted small" />
</a>
)}
{UserService.Instance.user &&
title="RSS"
rel="noopener"
>
- <svg class="icon text-muted small">
- <use xlinkHref="#icon-rss">#</use>
- </svg>
+ <Icon icon="rss" classes="text-muted small" />
</a>
)}
</div>
import autosize from 'autosize';
import { i18n } from '../i18next';
import { pictrsUri } from '../env';
+import { Icon, Spinner } from './icon';
interface MarkdownTextAreaProps {
initialContent: string;
disabled={this.props.disabled || this.state.loading}
>
{this.state.loading ? (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
) : (
<span>{this.props.buttonTitle}</span>
)}
data-tippy-content={i18n.t('bold')}
onClick={linkEvent(this, this.handleInsertBold)}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-bold"></use>
- </svg>
+ <Icon icon="bold" classes="icon-inline" />
</button>
<button
class="btn btn-sm text-muted"
data-tippy-content={i18n.t('italic')}
onClick={linkEvent(this, this.handleInsertItalic)}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-italic"></use>
- </svg>
+ <Icon icon="italic" classes="icon-inline" />
</button>
<button
class="btn btn-sm text-muted"
data-tippy-content={i18n.t('link')}
onClick={linkEvent(this, this.handleInsertLink)}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-link"></use>
- </svg>
+ <Icon icon="link" classes="icon-inline" />
</button>
<form class="btn btn-sm text-muted font-weight-bold">
<label
data-tippy-content={i18n.t('upload_image')}
>
{this.state.imageLoading ? (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
) : (
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-image"></use>
- </svg>
+ <Icon icon="image" classes="icon-inline" />
)}
</label>
<input
aria-label={i18n.t('header')}
onClick={linkEvent(this, this.handleInsertHeader)}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-header"></use>
- </svg>
+ <Icon icon="header" classes="icon-inline" />
</button>
<button
class="btn btn-sm text-muted"
aria-label={i18n.t('strikethrough')}
onClick={linkEvent(this, this.handleInsertStrikethrough)}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-strikethrough"></use>
- </svg>
+ <Icon icon="strikethrough" classes="icon-inline" />
</button>
<button
class="btn btn-sm text-muted"
aria-label={i18n.t('quote')}
onClick={linkEvent(this, this.handleInsertQuote)}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-format_quote"></use>
- </svg>
+ <Icon icon="format_quote" classes="icon-inline" />
</button>
<button
class="btn btn-sm text-muted"
aria-label={i18n.t('list')}
onClick={linkEvent(this, this.handleInsertList)}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-list"></use>
- </svg>
+ <Icon icon="list" classes="icon-inline" />
</button>
<button
class="btn btn-sm text-muted"
aria-label={i18n.t('code')}
onClick={linkEvent(this, this.handleInsertCode)}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-code"></use>
- </svg>
+ <Icon icon="code" classes="icon-inline" />
</button>
<button
class="btn btn-sm text-muted"
aria-label={i18n.t('subscript')}
onClick={linkEvent(this, this.handleInsertSubscript)}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-subscript"></use>
- </svg>
+ <Icon icon="subscript" classes="icon-inline" />
</button>
<button
class="btn btn-sm text-muted"
aria-label={i18n.t('superscript')}
onClick={linkEvent(this, this.handleInsertSuperscript)}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-superscript"></use>
- </svg>
+ <Icon icon="superscript" classes="icon-inline" />
</button>
<button
class="btn btn-sm text-muted"
aria-label={i18n.t('spoiler')}
onClick={linkEvent(this, this.handleInsertSpoiler)}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-alert-triangle"></use>
- </svg>
+ <Icon icon="alert-triangle" classes="icon-inline" />
</button>
<a
href={markdownHelpUrl}
title={i18n.t('formatting_help')}
rel="noopener"
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-help-circle"></use>
- </svg>
+ <Icon icon="help-circle" classes="icon-inline" />
</a>
</div>
</div>
import { InitialFetchRequest } from 'shared/interfaces';
import { UserListing } from './user-listing';
import { CommunityLink } from './community-link';
+import { Spinner } from './icon';
enum ModlogEnum {
ModRemovePost,
path={this.context.router.route.match.url}
/>
{this.state.loading ? (
- <h5 class="">
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <h5>
+ <Spinner />
</h5>
) : (
<div>
import moment from 'moment';
import { getMomentLanguage, capitalizeFirstLetter } from '../utils';
import { i18n } from '../i18next';
+import { Icon } from './icon';
interface MomentTimeProps {
data: {
)} ${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>
+ <Icon icon="edit-2" classes="icon-inline mr-1" />
{moment.utc(this.props.data.updated).fromNow(!this.props.showAgo)}
</span>
);
} from '../utils';
import { i18n } from '../i18next';
import { PictrsImage } from './pictrs-image';
+import { Icon } from './icon';
interface NavbarProps {
site_res: GetSiteResponse;
to="/inbox"
title={i18n.t('inbox')}
>
- <svg class="icon">
- <use xlinkHref="#icon-bell"></use>
- </svg>
+ <Icon icon="bell" />
{this.state.unreadCount > 0 && (
<span class="mx-1 badge badge-light">
{this.state.unreadCount}
onClick={linkEvent(this, this.expandNavbar)}
data-tippy-content={i18n.t('expand_here')}
>
- <svg class="icon">
- <use xlinkHref="#icon-menu"></use>
- </svg>
+ <Icon icon="menu" />
</button>
<div
className={`${!this.state.expanded && 'collapse'} navbar-collapse`}
title={i18n.t('support_lemmy')}
href={supportLemmyUrl}
>
- <svg class="icon small">
- <use xlinkHref="#icon-beer"></use>
- </svg>
+ <Icon icon="beer" classes="small" />
</a>
</li>
</ul>
to={`/admin`}
title={i18n.t('admin_settings')}
>
- <svg class="icon">
- <use xlinkHref="#icon-settings"></use>
- </svg>
+ <Icon icon="settings" />
</Link>
</li>
)}
class="px-1 btn btn-link"
style="color: var(--gray)"
>
- <svg class="icon">
- <use xlinkHref="#icon-search"></use>
- </svg>
+ <Icon icon="search" />
</button>
</form>
)}
to="/inbox"
title={i18n.t('inbox')}
>
- <svg class="icon">
- <use xlinkHref="#icon-bell"></use>
- </svg>
+ <Icon icon="bell" />
{this.state.unreadCount > 0 && (
<span class="ml-1 badge badge-light">
{this.state.unreadCount}
} from '../utils';
import { i18n } from '../i18next';
import { HtmlTags } from './html-tags';
+import { Spinner } from './icon';
interface State {
passwordChangeForm: PasswordChangeForm;
<div class="col-sm-10">
<button type="submit" class="btn btn-secondary">
{this.state.loading ? (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
) : (
capitalizeFirstLetter(i18n.t('save'))
)}
import { Prompt } from 'inferno-router';
import { PostListings } from './post-listings';
import { MarkdownTextArea } from './markdown-textarea';
+import { Icon, Spinner } from './icon';
import { Subscription } from 'rxjs';
import {
CreatePost,
} d-inline-block float-right text-muted font-weight-bold`}
data-tippy-content={i18n.t('upload_image')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-image"></use>
- </svg>
+ <Icon icon="image" classes="icon-inline" />
</label>
<input
id="file-upload"
{i18n.t('archive_link')}
</a>
)}
- {this.state.imageLoading && (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
- )}
+ {this.state.imageLoading && <Spinner />}
{isImage(this.state.postForm.url) && (
<img src={this.state.postForm.url} class="img-fluid" alt="" />
)}
class="btn btn-secondary mr-2"
>
{this.state.loading ? (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
) : this.props.post_view ? (
capitalizeFirstLetter(i18n.t('save'))
) : (
import { UserListing } from './user-listing';
import { CommunityLink } from './community-link';
import { PictrsImage } from './pictrs-image';
+import { Icon } from './icon';
import {
md,
mdToHtml,
aria-label={i18n.t('expand_here')}
>
{this.imgThumb(this.getImageSrc())}
- <svg class="icon mini-overlay">
- <use xlinkHref="#icon-image"></use>
- </svg>
+ <Icon icon="image" classes="mini-overlay" />
</div>
);
} else if (post.thumbnail_url) {
title={post.url}
>
{this.imgThumb(this.getImageSrc())}
- <svg class="icon mini-overlay">
- <use xlinkHref="#icon-external-link"></use>
- </svg>
+ <Icon icon="external-link" classes="mini-overlay" />
</a>
);
} else if (post.url) {
rel="noopener"
>
<div class="thumbnail rounded bg-light d-flex justify-content-center">
- <svg class="icon d-flex align-items-center">
- <use xlinkHref="#icon-external-link"></use>
- </svg>
+ <Icon icon="external-link" classes="d-flex align-items-center" />
</div>
</a>
);
title={i18n.t('comments')}
>
<div class="thumbnail rounded bg-light d-flex justify-content-center">
- <svg class="icon d-flex align-items-center">
- <use xlinkHref="#icon-message-square"></use>
- </svg>
+ <Icon icon="message-square" classes="d-flex align-items-center" />
</div>
</Link>
);
aria-label={i18n.t('upvote')}
to={`/post/${post_view.post.id}`}
>
- <svg class="mr-1 icon icon-inline">
- <use xlinkHref="#icon-book-open"></use>
- </svg>
+ <Icon icon="book-open" classes="icon-inline mr-1" />
</Link>
</li>
</>
data-tippy-content={i18n.t('upvote')}
aria-label={i18n.t('upvote')}
>
- <svg class="icon upvote">
- <use xlinkHref="#icon-arrow-up1"></use>
- </svg>
+ <Icon icon="arrow-up1" classes="upvote" />
</button>
<div
class={`unselectable pointer font-weight-bold text-muted px-1`}
data-tippy-content={i18n.t('downvote')}
aria-label={i18n.t('downvote')}
>
- <svg class="icon downvote">
- <use xlinkHref="#icon-arrow-down1"></use>
- </svg>
+ <Icon icon="arrow-down1" classes="downvote" />
</button>
)}
</div>
data-tippy-content={i18n.t('expand_here')}
onClick={linkEvent(this, this.handleImageExpandClick)}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-plus-square"></use>
- </svg>
+ <Icon icon="plus-square" classes="icon-inline" />
</span>
) : (
<span>
class="text-monospace unselectable pointer ml-2 text-muted small"
onClick={linkEvent(this, this.handleImageExpandClick)}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-minus-square"></use>
- </svg>
+ <Icon icon="minus-square" classes="icon-inline" />
</span>
<div>
<span
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>
+ <Icon icon="trash" classes="icon-inline text-danger" />
</small>
)}
{post.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>
+ <Icon icon="lock" classes="icon-inline text-danger" />
</small>
)}
{post.stickied && (
className="unselectable pointer ml-2 text-muted font-italic"
data-tippy-content={i18n.t('stickied')}
>
- <svg class={`icon icon-inline text-primary`}>
- <use xlinkHref="#icon-pin"></use>
- </svg>
+ <Icon icon="pin" classes="icon-inline text-primary" />
</small>
)}
{post.nsfw && (
})}
to={`/post/${post_view.post.id}`}
>
- <svg class="mr-1 icon icon-inline">
- <use xlinkHref="#icon-message-square"></use>
- </svg>
+ <Icon icon="message-square" classes="icon-inline mr-1" />
{i18n.t('number_of_comments', {
count: post_view.counts.comments,
})}
aria-label={i18n.t('downvote')}
>
<small>
- <svg class="icon icon-inline mr-1">
- <use xlinkHref="#icon-arrow-down1"></use>
- </svg>
+ <Icon icon="arrow-down1" classes="icon-inline mr-1" />
<span>{this.state.downvotes}</span>
</small>
</button>
aria-label={post_view.saved ? i18n.t('unsave') : i18n.t('save')}
>
<small>
- <svg
- class={`icon icon-inline ${
- post_view.saved && 'text-warning'
- }`}
- >
- <use xlinkHref="#icon-star"></use>
- </svg>
+ <Icon
+ icon="star"
+ classes={`icon-inline ${post_view.saved && 'text-warning'}`}
+ />
</small>
</button>
)}
onClick={linkEvent(this, this.handlePostLike)}
aria-label={i18n.t('upvote')}
>
- <svg class="small icon icon-inline mr-2">
- <use xlinkHref="#icon-arrow-up1"></use>
- </svg>
+ <Icon icon="arrow-up1" classes="icon-inline small mr-2" />
{this.state.upvotes}
</button>
{this.props.enableDownvotes && (
data-tippy-content={this.pointsTippy}
aria-label={i18n.t('downvote')}
>
- <svg class="small icon icon-inline mr-2">
- <use xlinkHref="#icon-arrow-down1"></use>
- </svg>
+ <Icon icon="arrow-down1" classes="icon-inline small mr-2" />
{this.state.downvotes !== 0 && (
<span>{this.state.downvotes}</span>
)}
post_view.saved ? i18n.t('unsave') : i18n.t('save')
}
>
- <svg
- class={`icon icon-inline ${post_view.saved && 'text-warning'}`}
- >
- <use xlinkHref="#icon-star"></use>
- </svg>
+ <Icon
+ icon="star"
+ classes={`icon-inline ${post_view.saved && 'text-warning'}`}
+ />
</button>
{!this.state.showMoreMobile && this.props.showBody && (
aria-label={i18n.t('more')}
data-tippy-content={i18n.t('more')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-more-vertical"></use>
- </svg>
+ <Icon icon="more-vertical" classes="icon-inline" />
</button>
)}
{this.state.showMoreMobile && this.postActions(mobile)}
post_view.saved ? i18n.t('unsave') : i18n.t('save')
}
>
- <svg
- class={`icon icon-inline ${
- post_view.saved && 'text-warning'
- }`}
- >
- <use xlinkHref="#icon-star"></use>
- </svg>
+ <Icon
+ icon="star"
+ classes={`icon-inline ${post_view.saved && 'text-warning'}`}
+ />
</button>
)}
<Link
to={`/create_post${this.crossPostParams}`}
title={i18n.t('cross_post')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-copy"></use>
- </svg>
+ <Icon icon="copy" classes="icon-inline" />
</Link>
</>
)}
onClick={linkEvent(this, this.handleEditClick)}
data-tippy-content={i18n.t('edit')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-edit"></use>
- </svg>
+ <Icon icon="edit" classes="icon-inline" />
</button>
<button
class="btn btn-link btn-animate text-muted py-0"
!post_view.post.deleted ? i18n.t('delete') : i18n.t('restore')
}
>
- <svg
- class={`icon icon-inline ${
+ <Icon
+ icon="trash"
+ classes={`icon-inline ${
post_view.post.deleted && 'text-danger'
}`}
- >
- <use xlinkHref="#icon-trash"></use>
- </svg>
+ />
</button>
</>
)}
data-tippy-content={i18n.t('more')}
aria-label={i18n.t('more')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-more-vertical"></use>
- </svg>
+ <Icon icon="more-vertical" classes="icon-inline" />
</button>
) : (
<>
onClick={linkEvent(this, this.handleViewSource)}
data-tippy-content={i18n.t('view_source')}
>
- <svg
- class={`icon icon-inline ${
+ <Icon
+ icon="file-text"
+ classes={`icon-inline ${
this.state.viewSource && 'text-success'
}`}
- >
- <use xlinkHref="#icon-file-text"></use>
- </svg>
+ />
</button>
)}
{this.canModOnSelf && (
post_view.post.locked ? i18n.t('unlock') : i18n.t('lock')
}
>
- <svg
- class={`icon icon-inline ${
+ <Icon
+ icon="lock"
+ classes={`icon-inline ${
post_view.post.locked && 'text-danger'
}`}
- >
- <use xlinkHref="#icon-lock"></use>
- </svg>
+ />
</button>
<button
class="btn btn-link btn-animate text-muted py-0"
: i18n.t('sticky')
}
>
- <svg
- class={`icon icon-inline ${
+ <Icon
+ icon="pin"
+ classes={`icon-inline ${
post_view.post.stickied && 'text-success'
}`}
- >
- <use xlinkHref="#icon-pin"></use>
- </svg>
+ />
</button>
</>
)}
import { Component, linkEvent } from 'inferno';
import { HtmlTags } from './html-tags';
+import { Spinner } from './icon';
import { Subscription } from 'rxjs';
import {
UserOperation,
<div class="container">
{this.state.loading ? (
<h5>
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
</h5>
) : (
<div class="row">
} from '../utils';
import { UserListing } from './user-listing';
import { MarkdownTextArea } from './markdown-textarea';
+import { Icon, Spinner } from './icon';
import { i18n } from '../i18next';
import { T } from 'inferno-i18next';
data-tippy-content={i18n.t('disclaimer')}
aria-label={i18n.t('disclaimer')}
>
- <svg class={`icon icon-inline`}>
- <use xlinkHref="#icon-alert-triangle"></use>
- </svg>
+ <Icon icon="alert-triangle" classes="icon-inline" />
</span>
</label>
<div class="col-sm-10">
disabled={this.state.loading}
>
{this.state.loading ? (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
) : this.props.privateMessage ? (
capitalizeFirstLetter(i18n.t('save'))
) : (
import { MomentTime } from './moment-time';
import { PrivateMessageForm } from './private-message-form';
import { UserListing } from './user-listing';
+import { Icon } from './icon';
import { i18n } from '../i18next';
interface PrivateMessageState {
onClick={linkEvent(this, this.handleMessageCollapse)}
>
{this.state.collapsed ? (
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-plus-square"></use>
- </svg>
+ <Icon icon="plus-square" classes="icon-inline" />
) : (
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-minus-square"></use>
- </svg>
+ <Icon icon="minus-square" classes="icon-inline" />
)}
</div>
</li>
: i18n.t('mark_as_read')
}
>
- <svg
- class={`icon icon-inline ${
+ <Icon
+ icon="check"
+ classes={`icon-inline ${
message_view.private_message.read && 'text-success'
}`}
- >
- <use xlinkHref="#icon-check"></use>
- </svg>
+ />
</button>
</li>
<li className="list-inline-item">
data-tippy-content={i18n.t('reply')}
aria-label={i18n.t('reply')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-reply1"></use>
- </svg>
+ <Icon icon="reply1" classes="icon-inline" />
</button>
</li>
</>
data-tippy-content={i18n.t('edit')}
aria-label={i18n.t('edit')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-edit"></use>
- </svg>
+ <Icon icon="edit" classes="icon-inline" />
</button>
</li>
<li className="list-inline-item">
: i18n.t('restore')
}
>
- <svg
- class={`icon icon-inline ${
+ <Icon
+ icon="trash"
+ classes={`icon-inline ${
message_view.private_message.deleted &&
'text-danger'
}`}
- >
- <use xlinkHref="#icon-trash"></use>
- </svg>
+ />
</button>
</li>
</>
data-tippy-content={i18n.t('view_source')}
aria-label={i18n.t('view_source')}
>
- <svg
- class={`icon icon-inline ${
+ <Icon
+ icon="file-text"
+ classes={`icon-inline ${
this.state.viewSource && 'text-success'
}`}
- >
- <use xlinkHref="#icon-file-text"></use>
- </svg>
+ />
</button>
</li>
</ul>
} from '../utils';
import { PostListing } from './post-listing';
import { HtmlTags } from './html-tags';
+import { Spinner } from './icon';
import { UserListing } from './user-listing';
import { CommunityLink } from './community-link';
import { SortSelect } from './sort-select';
minLength={3}
/>
<button type="submit" class="btn btn-secondary mr-2 mb-2">
- {this.state.loading ? (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
- ) : (
- <span>{i18n.t('search')}</span>
- )}
+ {this.state.loading ? <Spinner /> : <span>{i18n.t('search')}</span>}
</button>
</form>
);
import { WebSocketService, UserService } from '../services';
import { wsUserOp, wsJsonToRes, toast, wsClient } from '../utils';
import { SiteForm } from './site-form';
+import { Spinner } from './icon';
import { i18n } from '../i18next';
interface State {
<div class="form-group row">
<div class="col-sm-10">
<button type="submit" class="btn btn-secondary">
- {this.state.userLoading ? (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
- ) : (
- i18n.t('sign_up')
- )}
+ {this.state.userLoading ? <Spinner /> : i18n.t('sign_up')}
</button>
</div>
</div>
import { UserListing } from './user-listing';
import { CommunityLink } from './community-link';
import { BannerIconHeader } from './banner-icon-header';
+import { Icon } from './icon';
import { i18n } from '../i18next';
interface SidebarProps {
href="#"
onClick={linkEvent(community.id, this.handleUnsubscribe)}
>
- <svg class="text-success mr-1 icon icon-inline">
- <use xlinkHref="#icon-check"></use>
- </svg>
+ <Icon icon="check" classes="icon-inline text-success mr-1" />
{i18n.t('joined')}
</a>
)}
data-tippy-content={i18n.t('edit')}
aria-label={i18n.t('edit')}
>
- <svg class="icon icon-inline">
- <use xlinkHref="#icon-edit"></use>
- </svg>
+ <Icon icon="edit" classes="icon-inline" />
</span>
</li>
{!this.amCreator &&
: i18n.t('restore')
}
>
- <svg
- class={`icon icon-inline ${
+ <Icon
+ icon="trash"
+ classes={`icon-inline ${
community_view.community.deleted && 'text-danger'
}`}
- >
- <use xlinkHref="#icon-trash"></use>
- </svg>
+ />
</span>
</li>
)}
import { Component, linkEvent } from 'inferno';
import { Prompt } from 'inferno-router';
import { MarkdownTextArea } from './markdown-textarea';
+import { Spinner } from './icon';
import { ImageUploadForm } from './image-upload-form';
import { Site, EditSite } from 'lemmy-js-client';
import { WebSocketService } from '../services';
disabled={this.state.loading}
>
{this.state.loading ? (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
) : this.props.site ? (
capitalizeFirstLetter(i18n.t('save'))
) : (
import { Component, linkEvent } from 'inferno';
import { SortType } from 'lemmy-js-client';
import { sortingHelpUrl, randomStr } from '../utils';
+import { Icon } from './icon';
import { i18n } from '../i18next';
interface SortSelectProps {
class="custom-select w-auto mr-2 mb-2"
aria-label={i18n.t('sort_type')}
>
- <option disabled aria-hidden="true">{i18n.t('sort_type')}</option>
+ <option disabled aria-hidden="true">
+ {i18n.t('sort_type')}
+ </option>
{!this.props.hideHot && [
<option value={SortType.Hot}>{i18n.t('hot')}</option>,
<option value={SortType.Active}>{i18n.t('active')}</option>,
{i18n.t('most_comments')}
</option>
)}
- <option disabled aria-hidden="true">─────</option>
+ <option disabled aria-hidden="true">
+ ─────
+ </option>
<option value={SortType.TopDay}>{i18n.t('top_day')}</option>
<option value={SortType.TopWeek}>{i18n.t('top_week')}</option>
<option value={SortType.TopMonth}>{i18n.t('top_month')}</option>
rel="noopener"
title={i18n.t('sorting_help')}
>
- <svg class={`icon icon-inline`}>
- <use xlinkHref="#icon-help-circle"></use>
- </svg>
+ <Icon icon="help-circle" classes="icon-inline" />
</a>
</>
);
import moment from 'moment';
import { UserDetails } from './user-details';
import { MarkdownTextArea } from './markdown-textarea';
+import { Icon, Spinner } from './icon';
import { ImageUploadForm } from './image-upload-form';
import { BannerIconHeader } from './banner-icon-header';
import { CommunityLink } from './community-link';
<div class="container">
{this.state.loading ? (
<h5>
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
</h5>
) : (
<div class="row">
rel="noopener"
title="RSS"
>
- <svg class="icon mx-2 text-muted small">
- <use xlinkHref="#icon-rss">#</use>
- </svg>
+ <Icon icon="rss" classes="text-muted small mx-2" />
</a>
</div>
);
<MomentTime data={uv.user} showAgo ignoreUpdated />
</div>
<div className="d-flex align-items-center text-muted mb-2">
- <svg class="icon">
- <use xlinkHref="#icon-cake"></use>
- </svg>
+ <Icon icon="cake" />
<span className="ml-2">
{i18n.t('cake_day_title')}{' '}
{moment.utc(uv.user.published).local().format('MMM DD, YYYY')}
<div class="form-group">
<button type="submit" class="btn btn-block btn-secondary mr-4">
{this.state.userSettingsLoading ? (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
) : (
capitalizeFirstLetter(i18n.t('save'))
)}
onClick={linkEvent(this, this.handleDeleteAccount)}
>
{this.state.deleteAccountLoading ? (
- <svg class="icon icon-spinner spin">
- <use xlinkHref="#icon-spinner"></use>
- </svg>
+ <Spinner />
) : (
capitalizeFirstLetter(i18n.t('delete'))
)}