Refactoring thumbnails. Fixes #564
- Adding a default discussion thumbnail - Adding a cropping max-height, and consistent width. - Getting rid of hover overlays, in favor of top right content-type icon.
This commit is contained in:
parent
a215076358
commit
434bf35a55
3 changed files with 558 additions and 473 deletions
19
ui/assets/css/main.css
vendored
19
ui/assets/css/main.css
vendored
|
@ -131,8 +131,13 @@ blockquote {
|
||||||
}
|
}
|
||||||
|
|
||||||
.thumbnail {
|
.thumbnail {
|
||||||
max-height: 62px;
|
object-fit: cover;
|
||||||
max-width: 400px;
|
max-height: 80px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg.thumbnail {
|
||||||
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-s-hows {
|
.no-s-hows {
|
||||||
|
@ -188,6 +193,16 @@ hr {
|
||||||
border: unset;
|
border: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mini-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 2px;
|
||||||
|
background: rgba(0,0,0,.4);
|
||||||
|
border-bottom-left-radius: 0.25rem !important;
|
||||||
|
border-top-right-radius: 0.25rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
.link-overlay:hover {
|
.link-overlay:hover {
|
||||||
transition: .1s;
|
transition: .1s;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
198
ui/src/components/post-listing.tsx
vendored
198
ui/src/components/post-listing.tsx
vendored
|
@ -121,9 +121,12 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="row">
|
<div class="">
|
||||||
{!this.state.showEdit ? (
|
{!this.state.showEdit ? (
|
||||||
this.listing()
|
<>
|
||||||
|
{this.listing()}
|
||||||
|
{this.body()}
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<PostForm
|
<PostForm
|
||||||
|
@ -137,23 +140,105 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
imgThumbnail() {
|
body() {
|
||||||
|
return (
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
{this.state.url && this.props.showBody && this.state.iframely && (
|
||||||
|
<IFramelyCard iframely={this.state.iframely} />
|
||||||
|
)}
|
||||||
|
{this.props.showBody && this.props.post.body && (
|
||||||
|
<>
|
||||||
|
{this.state.viewSource ? (
|
||||||
|
<pre>{this.props.post.body}</pre>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
className="md-div"
|
||||||
|
dangerouslySetInnerHTML={mdToHtml(this.props.post.body)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
imgThumb() {
|
||||||
let post = this.props.post;
|
let post = this.props.post;
|
||||||
return (
|
return (
|
||||||
<object
|
<img
|
||||||
className={`img-fluid thumbnail rounded ${(post.nsfw ||
|
className={`img-fluid thumbnail rounded ${(post.nsfw ||
|
||||||
post.community_nsfw) &&
|
post.community_nsfw) &&
|
||||||
'img-blur'}`}
|
'img-blur'}`}
|
||||||
data={imageThumbnailer(this.state.thumbnail)}
|
src={imageThumbnailer(this.state.thumbnail)}
|
||||||
></object>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thumbnail() {
|
||||||
|
let post = this.props.post;
|
||||||
|
|
||||||
|
if (isImage(this.state.url)) {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
class="text-body pointer"
|
||||||
|
title={i18n.t('expand_here')}
|
||||||
|
onClick={linkEvent(this, this.handleImageExpandClick)}
|
||||||
|
>
|
||||||
|
{this.imgThumb()}
|
||||||
|
<svg class="icon mini-overlay">
|
||||||
|
<use xlinkHref="#icon-image"></use>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else if (this.state.thumbnail) {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
className="text-body"
|
||||||
|
href={this.state.url}
|
||||||
|
target="_blank"
|
||||||
|
title={this.state.url}
|
||||||
|
>
|
||||||
|
{this.imgThumb()}
|
||||||
|
<svg class="icon mini-overlay">
|
||||||
|
<use xlinkHref="#icon-external-link"></use>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
} else if (this.state.url && !this.state.thumbnail) {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
className="text-body"
|
||||||
|
href={this.state.url}
|
||||||
|
target="_blank"
|
||||||
|
title={this.state.url}
|
||||||
|
>
|
||||||
|
<svg class="icon thumbnail">
|
||||||
|
<use xlinkHref="#icon-external-link"></use>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
className="text-body"
|
||||||
|
to={`/post/${post.id}`}
|
||||||
|
title={i18n.t('comments')}
|
||||||
|
>
|
||||||
|
<svg class="icon thumbnail">
|
||||||
|
<use xlinkHref="#icon-bubble2"></use>
|
||||||
|
</svg>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
listing() {
|
listing() {
|
||||||
let post = this.props.post;
|
let post = this.props.post;
|
||||||
return (
|
return (
|
||||||
<div class="listing col-12">
|
<div class="row">
|
||||||
<div className={`vote-bar mr-2 float-left small text-center`}>
|
<div className={`vote-bar col-1 pr-0 small text-center`}>
|
||||||
<button
|
<button
|
||||||
className={`vote-animate btn btn-link p-0 ${
|
className={`vote-animate btn btn-link p-0 ${
|
||||||
this.state.my_vote == 1 ? 'text-info' : 'text-muted'
|
this.state.my_vote == 1 ? 'text-info' : 'text-muted'
|
||||||
|
@ -178,32 +263,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{this.state.thumbnail && !this.state.imageExpanded && (
|
{!this.state.imageExpanded && (
|
||||||
<div class="mx-2 mt-1 float-left position-relative">
|
<div class="col-2 pr-0 mt-1">
|
||||||
{isImage(this.state.url) ? (
|
<div class="position-relative">{this.thumbnail()}</div>
|
||||||
<span
|
|
||||||
class="text-body pointer"
|
|
||||||
title={i18n.t('expand_here')}
|
|
||||||
onClick={linkEvent(this, this.handleImageExpandClick)}
|
|
||||||
>
|
|
||||||
{this.imgThumbnail()}
|
|
||||||
<svg class="icon thumbnail rounded link-overlay hover-link">
|
|
||||||
<use xlinkHref="#icon-image"></use>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<a
|
|
||||||
className="text-body"
|
|
||||||
href={this.state.url}
|
|
||||||
target="_blank"
|
|
||||||
title={this.state.url}
|
|
||||||
>
|
|
||||||
{this.imgThumbnail()}
|
|
||||||
<svg class="icon thumbnail rounded link-overlay hover-link">
|
|
||||||
<use xlinkHref="#icon-external-link"></use>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{this.state.url && isVideo(this.state.url) && (
|
{this.state.url && isVideo(this.state.url) && (
|
||||||
|
@ -212,14 +274,16 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
muted
|
muted
|
||||||
loop
|
loop
|
||||||
controls
|
controls
|
||||||
class="mx-2 mt-1 float-left"
|
class="col-2 pr-0 mt-1"
|
||||||
height="100"
|
height="100"
|
||||||
width="150"
|
width="150"
|
||||||
>
|
>
|
||||||
<source src={this.state.url} type="video/mp4" />
|
<source src={this.state.url} type="video/mp4" />
|
||||||
</video>
|
</video>
|
||||||
)}
|
)}
|
||||||
<div className="ml-4">
|
<div class="col-9">
|
||||||
|
<div class="row">
|
||||||
|
<div className="col-12">
|
||||||
<div className="post-title">
|
<div className="post-title">
|
||||||
<h5 className="mb-0 d-inline">
|
<h5 className="mb-0 d-inline">
|
||||||
{this.props.showBody && this.state.url ? (
|
{this.props.showBody && this.state.url ? (
|
||||||
|
@ -280,16 +344,15 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
class="pointer"
|
class="pointer"
|
||||||
onClick={linkEvent(this, this.handleImageExpandClick)}
|
onClick={linkEvent(
|
||||||
|
this,
|
||||||
|
this.handleImageExpandClick
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<object
|
<img
|
||||||
class="img-fluid img-expanded"
|
class="img-fluid img-expanded"
|
||||||
data={this.state.thumbnail}
|
src={this.state.thumbnail}
|
||||||
>
|
/>
|
||||||
<svg class="icon thumbnail rounded placeholder">
|
|
||||||
<use xlinkHref="#icon-external-link"></use>
|
|
||||||
</svg>
|
|
||||||
</object>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
@ -323,7 +386,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="details ml-4">
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div className="details col-12">
|
||||||
<ul class="list-inline mb-0 text-muted small">
|
<ul class="list-inline mb-0 text-muted small">
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<span>{i18n.t('by')} </span>
|
<span>{i18n.t('by')} </span>
|
||||||
|
@ -339,7 +404,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
<span>{post.creator_name}</span>
|
<span>{post.creator_name}</span>
|
||||||
</Link>
|
</Link>
|
||||||
{this.isMod && (
|
{this.isMod && (
|
||||||
<span className="mx-1 badge badge-light">{i18n.t('mod')}</span>
|
<span className="mx-1 badge badge-light">
|
||||||
|
{i18n.t('mod')}
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
{this.isAdmin && (
|
{this.isAdmin && (
|
||||||
<span className="mx-1 badge badge-light">
|
<span className="mx-1 badge badge-light">
|
||||||
|
@ -389,7 +456,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</li>
|
</li>
|
||||||
{this.props.post.duplicates.map(post => (
|
{this.props.post.duplicates.map(post => (
|
||||||
<li className="list-inline-item mr-2">
|
<li className="list-inline-item mr-2">
|
||||||
<Link to={`/post/${post.id}`}>{post.community_name}</Link>
|
<Link to={`/post/${post.id}`}>
|
||||||
|
{post.community_name}
|
||||||
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
@ -433,7 +502,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
class="pointer"
|
class="pointer"
|
||||||
onClick={linkEvent(this, this.handleDeleteClick)}
|
onClick={linkEvent(this, this.handleDeleteClick)}
|
||||||
>
|
>
|
||||||
{!post.deleted ? i18n.t('delete') : i18n.t('restore')}
|
{!post.deleted
|
||||||
|
? i18n.t('delete')
|
||||||
|
: i18n.t('restore')}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</>
|
</>
|
||||||
|
@ -453,7 +524,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
class="pointer"
|
class="pointer"
|
||||||
onClick={linkEvent(this, this.handleModSticky)}
|
onClick={linkEvent(this, this.handleModSticky)}
|
||||||
>
|
>
|
||||||
{post.stickied ? i18n.t('unsticky') : i18n.t('sticky')}
|
{post.stickied
|
||||||
|
? i18n.t('unsticky')
|
||||||
|
: i18n.t('sticky')}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</>
|
</>
|
||||||
|
@ -471,7 +544,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
) : (
|
) : (
|
||||||
<span
|
<span
|
||||||
class="pointer"
|
class="pointer"
|
||||||
onClick={linkEvent(this, this.handleModRemoveSubmit)}
|
onClick={linkEvent(
|
||||||
|
this,
|
||||||
|
this.handleModRemoveSubmit
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{i18n.t('restore')}
|
{i18n.t('restore')}
|
||||||
</span>
|
</span>
|
||||||
|
@ -577,7 +653,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
) : (
|
) : (
|
||||||
<span
|
<span
|
||||||
class="pointer"
|
class="pointer"
|
||||||
onClick={linkEvent(this, this.handleModBanSubmit)}
|
onClick={linkEvent(
|
||||||
|
this,
|
||||||
|
this.handleModBanSubmit
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{i18n.t('unban_from_site')}
|
{i18n.t('unban_from_site')}
|
||||||
</span>
|
</span>
|
||||||
|
@ -648,9 +727,6 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
{this.state.url && this.props.showBody && this.state.iframely && (
|
|
||||||
<IFramelyCard iframely={this.state.iframely} />
|
|
||||||
)}
|
|
||||||
{this.state.showRemoveDialog && (
|
{this.state.showRemoveDialog && (
|
||||||
<form
|
<form
|
||||||
class="form-inline"
|
class="form-inline"
|
||||||
|
@ -695,18 +771,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
{this.props.showBody && post.body && (
|
</div>
|
||||||
<>
|
</div>
|
||||||
{this.state.viewSource ? (
|
|
||||||
<pre>{post.body}</pre>
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
className="md-div"
|
|
||||||
dangerouslySetInnerHTML={mdToHtml(post.body)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
4
ui/src/components/symbols.tsx
vendored
4
ui/src/components/symbols.tsx
vendored
|
@ -15,6 +15,10 @@ 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-bubble2" viewBox="0 0 32 32">
|
||||||
|
<title>bubble2</title>
|
||||||
|
<path d="M16 6c-1.717 0-3.375 0.271-4.928 0.804-1.46 0.502-2.76 1.211-3.863 2.108-2.069 1.681-3.209 3.843-3.209 6.088 0 1.259 0.35 2.481 1.039 3.63 0.711 1.185 1.781 2.268 3.093 3.133 0.949 0.625 1.587 1.623 1.755 2.747 0.056 0.375 0.091 0.753 0.105 1.129 0.233-0.194 0.461-0.401 0.684-0.624 0.755-0.755 1.774-1.172 2.828-1.172 0.168 0 0.336 0.011 0.505 0.032 0.655 0.083 1.325 0.126 1.99 0.126 1.717 0 3.375-0.271 4.928-0.804 1.46-0.502 2.76-1.211 3.863-2.108 2.069-1.681 3.209-3.843 3.209-6.088s-1.14-4.407-3.209-6.088c-1.104-0.897-2.404-1.606-3.863-2.108-1.553-0.534-3.211-0.804-4.928-0.804zM16 2v0c8.837 0 16 5.82 16 13s-7.163 13-16 13c-0.849 0-1.682-0.054-2.495-0.158-3.437 3.437-7.539 4.053-11.505 4.144v-0.841c2.142-1.049 4-2.961 4-5.145 0-0.305-0.024-0.604-0.068-0.897-3.619-2.383-5.932-6.024-5.932-10.103 0-7.18 7.163-13 16-13z"></path>
|
||||||
|
</symbol>
|
||||||
<symbol id="icon-image" viewBox="0 0 32 32">
|
<symbol id="icon-image" viewBox="0 0 32 32">
|
||||||
<title>image</title>
|
<title>image</title>
|
||||||
<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="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>
|
||||||
|
|
Reference in a new issue