margin-top: -60px;
}
+.img-icon {
+ width: 2rem; height: 2rem;
+}
+
.tribute-container ul {
margin: 0;
margin-top: 2px;
import { Component } from 'inferno';
+import { PictrsImage } from './pictrs-image';
interface BannerIconHeaderProps {
banner?: string;
render() {
return (
<div class="position-relative mb-2">
- {this.props.banner && (
- <img src={this.props.banner} class="banner img-fluid" />
- )}
+ {this.props.banner && <PictrsImage src={this.props.banner} />}
{this.props.icon && (
- <img
+ <PictrsImage
src={this.props.icon}
- className={`ml-2 mb-0 ${
- this.props.banner ? 'avatar-pushup' : ''
- } rounded-circle avatar-overlay`}
+ iconOverlay
+ pushup={!!this.props.banner}
/>
)}
</div>
import { Component } from 'inferno';
import { Link } from 'inferno-router';
import { Community } from 'lemmy-js-client';
-import { hostname, pictrsAvatarThumbnail, showAvatars } from '../utils';
+import { hostname, showAvatars } from '../utils';
+import { PictrsImage } from './pictrs-image';
interface CommunityOther {
name: string;
to={link}
>
{!this.props.hideAvatar && community.icon && showAvatars() && (
- <img
- style="width: 2rem; height: 2rem;"
- src={pictrsAvatarThumbnail(community.icon)}
- class="rounded-circle mr-2"
- />
+ <PictrsImage src={community.icon} icon />
)}
<span>{displayName}</span>
</Link>
} from 'lemmy-js-client';
import {
wsJsonToRes,
- pictrsAvatarThumbnail,
showAvatars,
fetchLimit,
toast,
wsSubscribe,
} from '../utils';
import { i18n } from '../i18next';
+import { PictrsImage } from './pictrs-image';
interface NavbarProps {
site: GetSiteResponse;
to="/"
>
{this.props.site.site.icon && showAvatars() && (
- <img
- src={pictrsAvatarThumbnail(this.props.site.site.icon)}
- height="32"
- width="32"
- class="rounded-circle mr-2"
- />
+ <PictrsImage src={this.props.site.site.icon} icon />
)}
{this.props.site.site.name}
</Link>
>
<span>
{user.avatar && showAvatars() && (
- <img
- src={pictrsAvatarThumbnail(user.avatar)}
- height="32"
- width="32"
- class="rounded-circle mr-2"
- />
+ <PictrsImage src={user.avatar} icon />
)}
{user.preferred_username
? user.preferred_username
--- /dev/null
+import { Component } from 'inferno';
+
+const iconThumbnailSize = 96;
+const thumbnailSize = 256;
+
+interface PictrsImageProps {
+ src: string;
+ icon?: boolean;
+ thumbnail?: boolean;
+ nsfw?: boolean;
+ iconOverlay?: boolean;
+ pushup?: boolean;
+}
+
+export class PictrsImage extends Component<PictrsImageProps, any> {
+ constructor(props: any, context: any) {
+ super(props, context);
+ }
+
+ render() {
+ return (
+ <picture>
+ <source srcSet={this.src('webp')} type="image/webp" />
+ <source srcSet={this.src('jpg')} type="image/jpeg" />
+ <img
+ src={this.src('jpg')}
+ className={`img-fluid
+ ${this.props.thumbnail ? 'thumbnail rounded ' : 'img-expanded '}
+ ${this.props.thumbnail && this.props.nsfw && 'img-blur '}
+ ${this.props.icon && 'rounded-circle img-icon mr-2 '}
+ ${this.props.iconOverlay && 'ml-2 mb-0 rounded-circle avatar-overlay '}
+ ${this.props.pushup && 'avatar-pushup '}
+ `}
+ />
+ </picture>
+ );
+ }
+
+ src(format: string): string {
+ // sample url:
+ // http://localhost:8535/pictrs/image/file.png?thumbnail=256&format=jpg
+
+ let split = this.props.src.split('/pictrs/image/');
+
+ // If theres not multiple, then its not a pictrs image
+ if (split.length == 1) {
+ return this.props.src;
+ }
+
+ let host = split[0];
+ let path = split[1];
+
+ let params = { format };
+
+ if (this.props.thumbnail) {
+ params['thumbnail'] = thumbnailSize;
+ } else if (this.props.icon) {
+ params['thumbnail'] = iconThumbnailSize;
+ }
+
+ let paramsStr = `?${new URLSearchParams(params).toString()}`;
+ let out = `${host}/pictrs/image/${path}${paramsStr}`;
+
+ return out;
+ }
+}
import { IFramelyCard } from './iframely-card';
import { UserListing } from './user-listing';
import { CommunityLink } from './community-link';
+import { PictrsImage } from './pictrs-image';
import {
md,
mdToHtml,
isImage,
isVideo,
getUnixTime,
- pictrsImage,
setupTippy,
hostname,
previewLines,
imgThumb(src: string) {
let post = this.props.post;
return (
- <img
- className={`img-fluid thumbnail rounded ${
- post.nsfw || post.community_nsfw ? 'img-blur' : ''
- }`}
+ <PictrsImage
src={src}
+ thumbnail
+ nsfw={post.nsfw || post.community_nsfw}
/>
);
}
- getImage(thumbnail: boolean = false) {
+ getImageSrc(): string {
let post = this.props.post;
if (isImage(post.url)) {
if (post.url.includes('pictrs')) {
- return pictrsImage(post.url, thumbnail);
+ return post.url;
} else if (post.thumbnail_url) {
- return pictrsImage(post.thumbnail_url, thumbnail);
+ return post.thumbnail_url;
} else {
return post.url;
}
} else if (post.thumbnail_url) {
- return pictrsImage(post.thumbnail_url, thumbnail);
+ return post.thumbnail_url;
} else {
return null;
}
data-tippy-content={i18n.t('expand_here')}
onClick={linkEvent(this, this.handleImageExpandClick)}
>
- {this.imgThumb(this.getImage(true))}
+ {this.imgThumb(this.getImageSrc())}
<svg class="icon mini-overlay">
<use xlinkHref="#icon-image"></use>
</svg>
rel="noopener"
title={post.url}
>
- {this.imgThumb(this.getImage(true))}
+ {this.imgThumb(this.getImageSrc())}
<svg class="icon mini-overlay">
<use xlinkHref="#icon-external-link"></use>
</svg>
class="pointer"
onClick={linkEvent(this, this.handleImageExpandClick)}
>
- <img class="img-fluid img-expanded" src={this.getImage()} />
+ <PictrsImage src={this.getImageSrc()} />
</span>
</div>
</span>
import { Component } from 'inferno';
import { Link } from 'inferno-router';
import { UserView } from 'lemmy-js-client';
-import {
- pictrsAvatarThumbnail,
- showAvatars,
- hostname,
- isCakeDay,
-} from '../utils';
+import { showAvatars, hostname, isCakeDay } from '../utils';
import { CakeDay } from './cake-day';
+import { PictrsImage } from './pictrs-image';
export interface UserOther {
name: string;
to={link}
>
{!this.props.hideAvatar && user.avatar && showAvatars() && (
- <img
- style="width: 2rem; height: 2rem;"
- src={pictrsAvatarThumbnail(user.avatar)}
- class="rounded-circle mr-2"
- />
+ <PictrsImage src={user.avatar} icon />
)}
<span>{displayName}</span>
</Link>
return ret;
}
-export function pictrsAvatarThumbnail(src: string): string {
- // sample url: http://localhost:8535/pictrs/image/thumbnail256/gs7xuu.jpg
- let split = src.split('/pictrs/image');
- let out = `${split[0]}/pictrs/image/${
- canUseWebP() ? 'webp/' : ''
- }thumbnail96${split[1]}`;
- return out;
-}
-
export function showAvatars(): boolean {
return (
(UserService.Instance.user && UserService.Instance.user.show_avatars) ||
);
}
-// Converts to image thumbnail
-export function pictrsImage(hash: string, thumbnail: boolean = false): string {
- let root = `/pictrs/image`;
-
- // Necessary for other servers / domains
- if (hash.includes('pictrs')) {
- let split = hash.split('/pictrs/image/');
- root = `${split[0]}/pictrs/image`;
- hash = split[1];
- }
-
- let out = `${root}/${canUseWebP() ? 'webp/' : ''}${
- thumbnail ? 'thumbnail256/' : ''
- }${hash}`;
- return out;
-}
-
export function isCommentType(
item: Comment | PrivateMessage | Post
): item is Comment {
return cUrl.port ? `${cUrl.hostname}:${cUrl.port}` : `${cUrl.hostname}`;
}
-function canUseWebP() {
- // TODO pictshare might have a webp conversion bug, try disabling this
- return false;
-
- // var elem = document.createElement('canvas');
- // if (!!(elem.getContext && elem.getContext('2d'))) {
- // var testString = !(window.mozInnerScreenX == null) ? 'png' : 'webp';
- // // was able or not to get WebP representation
- // return (
- // elem.toDataURL('image/webp').startsWith('data:image/' + testString)
- // );
- // }
-
- // // very old browser like IE 8, canvas not supported
- // return false;
-}
-
export function validTitle(title?: string): boolean {
// Initial title is null, minimum length is taken care of by textarea's minLength={3}
if (title === null || title.length < 3) return true;