A better mobile view, upgrading darkly theme.
This commit is contained in:
parent
6fa9dc599c
commit
c5443b6e82
5 changed files with 841 additions and 697 deletions
104
ui/assets/css/themes/_variables.darkly.scss
vendored
Normal file
104
ui/assets/css/themes/_variables.darkly.scss
vendored
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
|
||||||
|
$white: #fff;
|
||||||
|
$gray-100: #f8f9fa;
|
||||||
|
$gray-200: #ebebeb;
|
||||||
|
$gray-300: #dee2e6;
|
||||||
|
$gray-400: #ced4da;
|
||||||
|
$gray-500: #adb5bd;
|
||||||
|
$gray-600: #888;
|
||||||
|
$gray-700: #444;
|
||||||
|
$gray-800: #303030;
|
||||||
|
$gray-900: #222;
|
||||||
|
$black: #000;
|
||||||
|
$blue: #375a7f;
|
||||||
|
$indigo: #6610f2;
|
||||||
|
$purple: #6f42c1;
|
||||||
|
$pink: #e83e8c;
|
||||||
|
$red: #e74c3c;
|
||||||
|
$orange: #fd7e14;
|
||||||
|
$yellow: #f39c12;
|
||||||
|
$green: #00bc8c;
|
||||||
|
$teal: #20c997;
|
||||||
|
$cyan: #3498db;
|
||||||
|
$primary: $blue;
|
||||||
|
$secondary: $gray-700;
|
||||||
|
$success: $green;
|
||||||
|
$info: $cyan;
|
||||||
|
$warning: $yellow;
|
||||||
|
$danger: $red;
|
||||||
|
$dark: $gray-300;
|
||||||
|
$yiq-contrasted-threshold: 175;
|
||||||
|
$body-bg: $gray-900;
|
||||||
|
$body-color: $gray-300;
|
||||||
|
$link-color: $success;
|
||||||
|
$font-family-sans-serif: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||||
|
$font-size-base: 0.9375rem;
|
||||||
|
$h1-font-size: 3rem;
|
||||||
|
$h2-font-size: 2.5rem;
|
||||||
|
$h3-font-size: 2rem;
|
||||||
|
$text-muted: $gray-600;
|
||||||
|
$table-accent-bg: $gray-800;
|
||||||
|
$table-border-color: $gray-700;
|
||||||
|
$input-border-color: $body-bg;
|
||||||
|
$input-group-addon-color: $gray-500;
|
||||||
|
$input-group-addon-bg: $gray-700;
|
||||||
|
$custom-file-color: $gray-500;
|
||||||
|
$custom-file-border-color: $body-bg;
|
||||||
|
$dropdown-bg: $gray-900;
|
||||||
|
$dropdown-border-color: $gray-700;
|
||||||
|
$dropdown-divider-bg: $gray-700;
|
||||||
|
$dropdown-link-color: $white;
|
||||||
|
$dropdown-link-hover-color: $white;
|
||||||
|
$dropdown-link-hover-bg: $primary;
|
||||||
|
$nav-link-padding-x: 2rem;
|
||||||
|
$nav-link-disabled-color: $gray-500;
|
||||||
|
$nav-tabs-border-color: $gray-700;
|
||||||
|
$nav-tabs-link-hover-border-color: $nav-tabs-border-color $nav-tabs-border-color transparent;
|
||||||
|
$nav-tabs-link-active-color: $white;
|
||||||
|
$nav-tabs-link-active-border-color: $nav-tabs-border-color $nav-tabs-border-color transparent;
|
||||||
|
$navbar-padding-y: 1rem;
|
||||||
|
$navbar-dark-color: rgba($white,.6);
|
||||||
|
$navbar-dark-hover-color: $white;
|
||||||
|
$navbar-light-color: rgba($white,.6);
|
||||||
|
$navbar-light-hover-color: $white;
|
||||||
|
$navbar-light-active-color: $white;
|
||||||
|
$navbar-light-toggler-border-color: rgba($gray-900, .1);
|
||||||
|
$pagination-color: $white;
|
||||||
|
$pagination-bg: $success;
|
||||||
|
$pagination-border-width: 0;
|
||||||
|
$pagination-border-color: transparent;
|
||||||
|
$pagination-hover-color: $white;
|
||||||
|
$pagination-hover-bg: lighten($success, 10%);
|
||||||
|
$pagination-hover-border-color: transparent;
|
||||||
|
$pagination-active-bg: $pagination-hover-bg;
|
||||||
|
$pagination-active-border-color: transparent;
|
||||||
|
$pagination-disabled-color: $white;
|
||||||
|
$pagination-disabled-bg: darken($success, 15%);
|
||||||
|
$pagination-disabled-border-color: transparent;
|
||||||
|
$jumbotron-bg: $gray-800;
|
||||||
|
$card-cap-bg: $gray-700;
|
||||||
|
$card-bg: $gray-800;
|
||||||
|
$popover-bg: $gray-800;
|
||||||
|
$popover-header-bg: $gray-700;
|
||||||
|
$toast-background-color: $gray-700;
|
||||||
|
$toast-header-background-color: $gray-800;
|
||||||
|
$modal-content-bg: $gray-800;
|
||||||
|
$modal-content-border-color: $gray-700;
|
||||||
|
$modal-header-border-color: $gray-700;
|
||||||
|
$progress-bg: $gray-700;
|
||||||
|
$list-group-bg: $gray-800;
|
||||||
|
$list-group-border-color: $gray-700;
|
||||||
|
$list-group-hover-bg: $gray-700;
|
||||||
|
$breadcrumb-bg: $gray-700;
|
||||||
|
$close-color: $white;
|
||||||
|
$close-text-shadow: none;
|
||||||
|
$pre-color: inherit;
|
||||||
|
$mark-bg: #333;
|
||||||
|
$custom-select-bg: $secondary;
|
||||||
|
$custom-select-color: $white;
|
||||||
|
$input-bg: $secondary;
|
||||||
|
$input-color: $white;
|
||||||
|
$input-disabled-bg: darken($secondary, 10%);;
|
||||||
|
$light: $gray-800;
|
||||||
|
$navbar-light-brand-color: $navbar-dark-active-color;
|
||||||
|
$navbar-light-brand-hover-color: $navbar-dark-active-color;
|
36
ui/assets/css/themes/darkly.min.css
vendored
36
ui/assets/css/themes/darkly.min.css
vendored
File diff suppressed because one or more lines are too long
4
ui/src/components/navbar.tsx
vendored
4
ui/src/components/navbar.tsx
vendored
|
@ -184,7 +184,7 @@ export class Navbar extends Component<any, NavbarState> {
|
||||||
{!this.state.siteLoading ? (
|
{!this.state.siteLoading ? (
|
||||||
<Link
|
<Link
|
||||||
title={this.state.siteRes.version}
|
title={this.state.siteRes.version}
|
||||||
class="d-flex align-items-center navbar-brand mr-1"
|
class="d-flex align-items-center navbar-brand mr-md-3"
|
||||||
to="/"
|
to="/"
|
||||||
>
|
>
|
||||||
{this.state.siteRes.site.icon && showAvatars() && (
|
{this.state.siteRes.site.icon && showAvatars() && (
|
||||||
|
@ -235,7 +235,7 @@ export class Navbar extends Component<any, NavbarState> {
|
||||||
!this.state.expanded && 'collapse'
|
!this.state.expanded && 'collapse'
|
||||||
} navbar-collapse`}
|
} navbar-collapse`}
|
||||||
>
|
>
|
||||||
<ul class="ml-3 navbar-nav my-2 mr-auto">
|
<ul class="navbar-nav my-2 mr-auto">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<Link
|
<Link
|
||||||
class="nav-link"
|
class="nav-link"
|
||||||
|
|
392
ui/src/components/post-listing.tsx
vendored
392
ui/src/components/post-listing.tsx
vendored
|
@ -163,7 +163,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
className={`img-fluid thumbnail rounded ${
|
className={`img-fluid thumbnail rounded ${
|
||||||
(post.nsfw || post.community_nsfw) && 'img-blur'
|
post.nsfw || post.community_nsfw ? 'img-blur' : ''
|
||||||
}`}
|
}`}
|
||||||
src={src}
|
src={src}
|
||||||
/>
|
/>
|
||||||
|
@ -190,8 +190,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
|
|
||||||
if (isImage(post.url)) {
|
if (isImage(post.url)) {
|
||||||
return (
|
return (
|
||||||
<span
|
<div
|
||||||
class="text-body pointer"
|
class="text-body pointer d-inline-block position-relative"
|
||||||
data-tippy-content={i18n.t('expand_here')}
|
data-tippy-content={i18n.t('expand_here')}
|
||||||
onClick={linkEvent(this, this.handleImageExpandClick)}
|
onClick={linkEvent(this, this.handleImageExpandClick)}
|
||||||
>
|
>
|
||||||
|
@ -199,12 +199,12 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
<svg class="icon mini-overlay">
|
<svg class="icon mini-overlay">
|
||||||
<use xlinkHref="#icon-image"></use>
|
<use xlinkHref="#icon-image"></use>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (post.thumbnail_url) {
|
} else if (post.thumbnail_url) {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
className="text-body"
|
class="text-body d-inline-block position-relative"
|
||||||
href={post.url}
|
href={post.url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener"
|
rel="noopener"
|
||||||
|
@ -265,10 +265,93 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
listing() {
|
createdLine() {
|
||||||
let post = this.props.post;
|
let post = this.props.post;
|
||||||
return (
|
return (
|
||||||
<div class="row">
|
<ul class="list-inline mb-1 text-muted small">
|
||||||
|
<li className="list-inline-item">
|
||||||
|
<UserListing
|
||||||
|
user={{
|
||||||
|
name: post.creator_name,
|
||||||
|
preferred_username: post.creator_preferred_username,
|
||||||
|
avatar: post.creator_avatar,
|
||||||
|
id: post.creator_id,
|
||||||
|
local: post.creator_local,
|
||||||
|
actor_id: post.creator_actor_id,
|
||||||
|
published: post.creator_published,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{this.isMod && (
|
||||||
|
<span className="mx-1 badge badge-light">{i18n.t('mod')}</span>
|
||||||
|
)}
|
||||||
|
{this.isAdmin && (
|
||||||
|
<span className="mx-1 badge badge-light">{i18n.t('admin')}</span>
|
||||||
|
)}
|
||||||
|
{(post.banned_from_community || post.banned) && (
|
||||||
|
<span className="mx-1 badge badge-danger">{i18n.t('banned')}</span>
|
||||||
|
)}
|
||||||
|
{this.props.showCommunity && (
|
||||||
|
<span>
|
||||||
|
<span class="mx-1"> {i18n.t('to')} </span>
|
||||||
|
<CommunityLink
|
||||||
|
community={{
|
||||||
|
name: post.community_name,
|
||||||
|
id: post.community_id,
|
||||||
|
local: post.community_local,
|
||||||
|
actor_id: post.community_actor_id,
|
||||||
|
icon: post.community_icon,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
<li className="list-inline-item">•</li>
|
||||||
|
{post.url && !(hostname(post.url) == window.location.hostname) && (
|
||||||
|
<>
|
||||||
|
<li className="list-inline-item">
|
||||||
|
<a
|
||||||
|
className="text-muted font-italic"
|
||||||
|
href={post.url}
|
||||||
|
target="_blank"
|
||||||
|
title={post.url}
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
{hostname(post.url)}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li className="list-inline-item">•</li>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<li className="list-inline-item">
|
||||||
|
<span>
|
||||||
|
<MomentTime data={post} />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
{post.body && (
|
||||||
|
<>
|
||||||
|
<li className="list-inline-item">•</li>
|
||||||
|
<li className="list-inline-item">
|
||||||
|
{/* Using a link with tippy doesn't work on touch devices unfortunately */}
|
||||||
|
<Link
|
||||||
|
className="text-muted"
|
||||||
|
data-tippy-content={md.render(previewLines(post.body))}
|
||||||
|
data-tippy-allowHtml={true}
|
||||||
|
to={`/post/${post.id}`}
|
||||||
|
>
|
||||||
|
<svg class="mr-1 icon icon-inline">
|
||||||
|
<use xlinkHref="#icon-book-open"></use>
|
||||||
|
</svg>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
voteBar() {
|
||||||
|
return (
|
||||||
<div className={`vote-bar col-1 pr-0 small text-center`}>
|
<div className={`vote-bar col-1 pr-0 small text-center`}>
|
||||||
<button
|
<button
|
||||||
className={`btn-animate btn btn-link p-0 ${
|
className={`btn-animate btn btn-link p-0 ${
|
||||||
|
@ -301,18 +384,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!this.state.imageExpanded && (
|
);
|
||||||
<div class="col-3 col-sm-2 pr-0">
|
}
|
||||||
<div class="position-relative">{this.thumbnail()}</div>
|
|
||||||
</div>
|
postTitleLine() {
|
||||||
)}
|
let post = this.props.post;
|
||||||
<div
|
return (
|
||||||
class={`${this.state.imageExpanded ? 'col-12' : 'col-8 col-sm-9'}`}
|
<div className="post-title overflow-hidden">
|
||||||
>
|
<h5>
|
||||||
<div class="row">
|
|
||||||
<div className="col-12">
|
|
||||||
<div className="post-title">
|
|
||||||
<h5 className="mb-1 d-inline-block">
|
|
||||||
{this.props.showBody && post.url ? (
|
{this.props.showBody && post.url ? (
|
||||||
<a
|
<a
|
||||||
className={!post.stickied ? 'text-body' : 'text-primary'}
|
className={!post.stickied ? 'text-body' : 'text-primary'}
|
||||||
|
@ -332,23 +411,6 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
{post.name}
|
{post.name}
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</h5>
|
|
||||||
{post.url && !(hostname(post.url) == window.location.hostname) && (
|
|
||||||
<small class="d-inline-block">
|
|
||||||
<a
|
|
||||||
className="ml-2 text-muted font-italic"
|
|
||||||
href={post.url}
|
|
||||||
target="_blank"
|
|
||||||
title={post.url}
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
{hostname(post.url)}
|
|
||||||
<svg class="ml-1 icon icon-inline">
|
|
||||||
<use xlinkHref="#icon-external-link"></use>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
</small>
|
|
||||||
)}
|
|
||||||
{(isImage(post.url) || this.props.post.thumbnail_url) && (
|
{(isImage(post.url) || this.props.post.thumbnail_url) && (
|
||||||
<>
|
<>
|
||||||
{!this.state.imageExpanded ? (
|
{!this.state.imageExpanded ? (
|
||||||
|
@ -374,10 +436,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
class="pointer"
|
class="pointer"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(this, this.handleImageExpandClick)}
|
||||||
this,
|
|
||||||
this.handleImageExpandClick
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="img-fluid img-expanded"
|
class="img-fluid img-expanded"
|
||||||
|
@ -429,82 +488,15 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
{i18n.t('nsfw')}
|
{i18n.t('nsfw')}
|
||||||
</small>
|
</small>
|
||||||
)}
|
)}
|
||||||
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
</div>
|
}
|
||||||
<div class="row">
|
|
||||||
<div className="details col-12">
|
|
||||||
<ul class="list-inline mb-1 text-muted small">
|
|
||||||
<li className="list-inline-item">
|
|
||||||
<span>{i18n.t('by')} </span>
|
|
||||||
<UserListing
|
|
||||||
user={{
|
|
||||||
name: post.creator_name,
|
|
||||||
preferred_username: post.creator_preferred_username,
|
|
||||||
avatar: post.creator_avatar,
|
|
||||||
id: post.creator_id,
|
|
||||||
local: post.creator_local,
|
|
||||||
actor_id: post.creator_actor_id,
|
|
||||||
published: post.creator_published,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{this.isMod && (
|
commentsLine(showVotes: boolean = false) {
|
||||||
<span className="mx-1 badge badge-light">
|
let post = this.props.post;
|
||||||
{i18n.t('mod')}
|
return (
|
||||||
</span>
|
<ul class="d-flex align-items-center list-inline mb-1 text-muted small">
|
||||||
)}
|
|
||||||
{this.isAdmin && (
|
|
||||||
<span className="mx-1 badge badge-light">
|
|
||||||
{i18n.t('admin')}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{(post.banned_from_community || post.banned) && (
|
|
||||||
<span className="mx-1 badge badge-danger">
|
|
||||||
{i18n.t('banned')}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{this.props.showCommunity && (
|
|
||||||
<span>
|
|
||||||
<span> {i18n.t('to')} </span>
|
|
||||||
<CommunityLink
|
|
||||||
community={{
|
|
||||||
name: post.community_name,
|
|
||||||
id: post.community_id,
|
|
||||||
local: post.community_local,
|
|
||||||
actor_id: post.community_actor_id,
|
|
||||||
icon: post.community_icon,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</li>
|
|
||||||
<li className="list-inline-item">•</li>
|
|
||||||
<li className="list-inline-item">
|
|
||||||
<span>
|
|
||||||
<MomentTime data={post} />
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
{post.body && (
|
|
||||||
<>
|
|
||||||
<li className="list-inline-item">•</li>
|
|
||||||
<li className="list-inline-item">
|
|
||||||
{/* Using a link with tippy doesn't work on touch devices unfortunately */}
|
|
||||||
<Link
|
|
||||||
className="text-muted"
|
|
||||||
data-tippy-content={md.render(previewLines(post.body))}
|
|
||||||
data-tippy-allowHtml={true}
|
|
||||||
to={`/post/${post.id}`}
|
|
||||||
>
|
|
||||||
<svg class="mr-1 icon icon-inline">
|
|
||||||
<use xlinkHref="#icon-book-open"></use>
|
|
||||||
</svg>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
<ul class="list-inline mb-1 text-muted small">
|
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<Link
|
<Link
|
||||||
className="text-muted"
|
className="text-muted"
|
||||||
|
@ -521,7 +513,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
})}
|
})}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
{this.state.upvotes !== this.state.score && (
|
{(showVotes || this.state.upvotes !== this.state.score) && (
|
||||||
<>
|
<>
|
||||||
<li className="list-inline-item">•</li>
|
<li className="list-inline-item">•</li>
|
||||||
<span
|
<span
|
||||||
|
@ -529,26 +521,41 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
data-tippy-content={this.pointsTippy}
|
data-tippy-content={this.pointsTippy}
|
||||||
>
|
>
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<span className="text-muted">
|
<a
|
||||||
|
className={`btn-animate btn btn-link p-0 ${
|
||||||
|
this.state.my_vote == 1 ? 'text-info' : 'text-muted'
|
||||||
|
}`}
|
||||||
|
onClick={linkEvent(this, this.handlePostLike)}
|
||||||
|
>
|
||||||
<svg class="small icon icon-inline mr-1">
|
<svg class="small icon icon-inline mr-1">
|
||||||
<use xlinkHref="#icon-arrow-up"></use>
|
<use xlinkHref="#icon-arrow-up"></use>
|
||||||
</svg>
|
</svg>
|
||||||
{this.state.upvotes}
|
{this.state.upvotes}
|
||||||
</span>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<span className="text-muted">
|
<a
|
||||||
|
className={`btn-animate btn btn-link p-0 ${
|
||||||
|
this.state.my_vote == -1 ? 'text-danger' : 'text-muted'
|
||||||
|
}`}
|
||||||
|
onClick={linkEvent(this, this.handlePostDisLike)}
|
||||||
|
>
|
||||||
<svg class="small icon icon-inline mr-1">
|
<svg class="small icon icon-inline mr-1">
|
||||||
<use xlinkHref="#icon-arrow-down"></use>
|
<use xlinkHref="#icon-arrow-down"></use>
|
||||||
</svg>
|
</svg>
|
||||||
{this.state.downvotes}
|
{this.state.downvotes}
|
||||||
</span>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
{this.props.post.duplicates && (
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicatesLine() {
|
||||||
|
return (
|
||||||
|
this.props.post.duplicates && (
|
||||||
<ul class="list-inline mb-1 small text-muted">
|
<ul class="list-inline mb-1 small text-muted">
|
||||||
<>
|
<>
|
||||||
<li className="list-inline-item mr-2">
|
<li className="list-inline-item mr-2">
|
||||||
|
@ -556,14 +563,18 @@ 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}`}>
|
<Link to={`/post/${post.id}`}>{post.community_name}</Link>
|
||||||
{post.community_name}
|
|
||||||
</Link>
|
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
postActions() {
|
||||||
|
let post = this.props.post;
|
||||||
|
return (
|
||||||
<ul class="list-inline mb-1 text-muted font-weight-bold">
|
<ul class="list-inline mb-1 text-muted font-weight-bold">
|
||||||
{UserService.Instance.user && (
|
{UserService.Instance.user && (
|
||||||
<>
|
<>
|
||||||
|
@ -578,9 +589,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class={`icon icon-inline ${
|
class={`icon icon-inline ${post.saved && 'text-warning'}`}
|
||||||
post.saved && 'text-warning'
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<use xlinkHref="#icon-star"></use>
|
<use xlinkHref="#icon-star"></use>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -617,9 +626,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
class="btn btn-link btn-animate text-muted"
|
class="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(this, this.handleDeleteClick)}
|
onClick={linkEvent(this, this.handleDeleteClick)}
|
||||||
data-tippy-content={
|
data-tippy-content={
|
||||||
!post.deleted
|
!post.deleted ? i18n.t('delete') : i18n.t('restore')
|
||||||
? i18n.t('delete')
|
|
||||||
: i18n.t('restore')
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
@ -672,9 +679,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
class="btn btn-link btn-animate text-muted"
|
class="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(this, this.handleModLock)}
|
onClick={linkEvent(this, this.handleModLock)}
|
||||||
data-tippy-content={
|
data-tippy-content={
|
||||||
post.locked
|
post.locked ? i18n.t('unlock') : i18n.t('lock')
|
||||||
? i18n.t('unlock')
|
|
||||||
: i18n.t('lock')
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
@ -691,9 +696,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
class="btn btn-link btn-animate text-muted"
|
class="btn btn-link btn-animate text-muted"
|
||||||
onClick={linkEvent(this, this.handleModSticky)}
|
onClick={linkEvent(this, this.handleModSticky)}
|
||||||
data-tippy-content={
|
data-tippy-content={
|
||||||
post.stickied
|
post.stickied ? i18n.t('unsticky') : i18n.t('sticky')
|
||||||
? i18n.t('unsticky')
|
|
||||||
: i18n.t('sticky')
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
@ -713,20 +716,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
{!post.removed ? (
|
{!post.removed ? (
|
||||||
<span
|
<span
|
||||||
class="pointer"
|
class="pointer"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(this, this.handleModRemoveShow)}
|
||||||
this,
|
|
||||||
this.handleModRemoveShow
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{i18n.t('remove')}
|
{i18n.t('remove')}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span
|
<span
|
||||||
class="pointer"
|
class="pointer"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(this, this.handleModRemoveSubmit)}
|
||||||
this,
|
|
||||||
this.handleModRemoveSubmit
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{i18n.t('restore')}
|
{i18n.t('restore')}
|
||||||
</span>
|
</span>
|
||||||
|
@ -778,8 +775,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* 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">
|
||||||
{!this.state.showConfirmTransferCommunity ? (
|
{!this.state.showConfirmTransferCommunity ? (
|
||||||
<span
|
<span
|
||||||
|
@ -809,8 +805,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
class="pointer d-inline-block"
|
class="pointer d-inline-block"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(
|
||||||
this,
|
this,
|
||||||
this
|
this.handleCancelShowConfirmTransferCommunity
|
||||||
.handleCancelShowConfirmTransferCommunity
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{i18n.t('no')}
|
{i18n.t('no')}
|
||||||
|
@ -827,20 +822,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
{!post.banned ? (
|
{!post.banned ? (
|
||||||
<span
|
<span
|
||||||
class="pointer"
|
class="pointer"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(this, this.handleModBanShow)}
|
||||||
this,
|
|
||||||
this.handleModBanShow
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{i18n.t('ban_from_site')}
|
{i18n.t('ban_from_site')}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span
|
<span
|
||||||
class="pointer"
|
class="pointer"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(this, this.handleModBanSubmit)}
|
||||||
this,
|
|
||||||
this.handleModBanSubmit
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{i18n.t('unban_from_site')}
|
{i18n.t('unban_from_site')}
|
||||||
</span>
|
</span>
|
||||||
|
@ -881,10 +870,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
class="pointer d-inline-block mr-1"
|
class="pointer d-inline-block mr-1"
|
||||||
onClick={linkEvent(
|
onClick={linkEvent(this, this.handleTransferSite)}
|
||||||
this,
|
|
||||||
this.handleTransferSite
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{i18n.t('yes')}
|
{i18n.t('yes')}
|
||||||
</span>
|
</span>
|
||||||
|
@ -906,6 +892,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAndBanDialogs() {
|
||||||
|
let post = this.props.post;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
{this.state.showRemoveDialog && (
|
{this.state.showRemoveDialog && (
|
||||||
<form
|
<form
|
||||||
class="form-inline"
|
class="form-inline"
|
||||||
|
@ -950,10 +943,89 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
mobileThumbnail() {
|
||||||
|
return this.props.post.thumbnail_url || isImage(this.props.post.url) ? (
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-8">{this.postTitleLine()}</div>
|
||||||
|
<div class="col-4">
|
||||||
|
{/* Post body prev or thumbnail */}
|
||||||
|
{!this.state.imageExpanded && this.thumbnail()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
this.postTitleLine()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
showMobilePreview() {
|
||||||
|
return (
|
||||||
|
this.props.post.body &&
|
||||||
|
!this.props.showBody && (
|
||||||
|
<div
|
||||||
|
className="md-div mb-1"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: md.render(previewLines(this.props.post.body)),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
listing() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* The mobile view*/}
|
||||||
|
<div class="d-block d-sm-none">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
{this.createdLine()}
|
||||||
|
|
||||||
|
{/* If it has a thumbnail, do a right aligned thumbnail */}
|
||||||
|
{this.mobileThumbnail()}
|
||||||
|
|
||||||
|
{/* Show a preview of the post body */}
|
||||||
|
{this.showMobilePreview()}
|
||||||
|
|
||||||
|
{this.commentsLine(true)}
|
||||||
|
{this.duplicatesLine()}
|
||||||
|
{this.postActions()}
|
||||||
|
{this.removeAndBanDialogs()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* The larger view*/}
|
||||||
|
<div class="d-none d-sm-block">
|
||||||
|
<div class="row">
|
||||||
|
{this.voteBar()}
|
||||||
|
{!this.state.imageExpanded && (
|
||||||
|
<div class="col-sm-2 pr-0">
|
||||||
|
<div class="">{this.thumbnail()}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
class={`${
|
||||||
|
this.state.imageExpanded ? 'col-12' : 'col-12 col-sm-9'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div class="row">
|
||||||
|
<div className="col-12">
|
||||||
|
{this.postTitleLine()}
|
||||||
|
{this.createdLine()}
|
||||||
|
{this.commentsLine()}
|
||||||
|
{this.duplicatesLine()}
|
||||||
|
{this.postActions()}
|
||||||
|
{this.removeAndBanDialogs()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
ui/src/utils.ts
vendored
6
ui/src/utils.ts
vendored
|
@ -982,10 +982,12 @@ function randomHsl() {
|
||||||
|
|
||||||
export function previewLines(text: string, lines: number = 3): string {
|
export function previewLines(text: string, lines: number = 3): string {
|
||||||
// Use lines * 2 because markdown requires 2 lines
|
// Use lines * 2 because markdown requires 2 lines
|
||||||
return text
|
return (
|
||||||
|
text
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.slice(0, lines * 2)
|
.slice(0, lines * 2)
|
||||||
.join('\n');
|
.join('\n') + '...'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hostname(url: string): string {
|
export function hostname(url: string): string {
|
||||||
|
|
Loading…
Reference in a new issue