From: Dessalines <tyhou13@gmx.com> Date: Wed, 14 Oct 2020 19:36:37 +0000 (-0500) Subject: Making front end work w/ pictrs v2. Fixes #57 X-Git-Url: http://these/git/%24%7B%60data:application/static/gitweb.css?a=commitdiff_plain;h=40e1aca891dba8205de2e9e4c999ee9fd53799be;p=lemmy-ui.git Making front end work w/ pictrs v2. Fixes #57 --- diff --git a/src/assets/css/main.css b/src/assets/css/main.css index e289266..c86e8ec 100644 --- a/src/assets/css/main.css +++ b/src/assets/css/main.css @@ -297,6 +297,10 @@ br.big { margin-top: -60px; } +.img-icon { + width: 2rem; height: 2rem; +} + .tribute-container ul { margin: 0; margin-top: 2px; diff --git a/src/shared/components/banner-icon-header.tsx b/src/shared/components/banner-icon-header.tsx index 8c0eedb..cb84eb2 100644 --- a/src/shared/components/banner-icon-header.tsx +++ b/src/shared/components/banner-icon-header.tsx @@ -1,4 +1,5 @@ import { Component } from 'inferno'; +import { PictrsImage } from './pictrs-image'; interface BannerIconHeaderProps { banner?: string; @@ -13,15 +14,12 @@ export class BannerIconHeader extends Component<BannerIconHeaderProps, any> { 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> diff --git a/src/shared/components/community-link.tsx b/src/shared/components/community-link.tsx index 003f61e..db69f44 100644 --- a/src/shared/components/community-link.tsx +++ b/src/shared/components/community-link.tsx @@ -1,7 +1,8 @@ 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; @@ -47,11 +48,7 @@ export class CommunityLink extends Component<CommunityLinkProps, any> { 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> diff --git a/src/shared/components/navbar.tsx b/src/shared/components/navbar.tsx index e3c27bd..04dae46 100644 --- a/src/shared/components/navbar.tsx +++ b/src/shared/components/navbar.tsx @@ -20,7 +20,6 @@ import { } from 'lemmy-js-client'; import { wsJsonToRes, - pictrsAvatarThumbnail, showAvatars, fetchLimit, toast, @@ -32,6 +31,7 @@ import { wsSubscribe, } from '../utils'; import { i18n } from '../i18next'; +import { PictrsImage } from './pictrs-image'; interface NavbarProps { site: GetSiteResponse; @@ -190,12 +190,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> { 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> @@ -345,12 +340,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> { > <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 diff --git a/src/shared/components/pictrs-image.tsx b/src/shared/components/pictrs-image.tsx new file mode 100644 index 0000000..471a0b7 --- /dev/null +++ b/src/shared/components/pictrs-image.tsx @@ -0,0 +1,66 @@ +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; + } +} diff --git a/src/shared/components/post-listing.tsx b/src/shared/components/post-listing.tsx index 52c40d4..6545b2a 100644 --- a/src/shared/components/post-listing.tsx +++ b/src/shared/components/post-listing.tsx @@ -24,6 +24,7 @@ import { PostForm } from './post-form'; import { IFramelyCard } from './iframely-card'; import { UserListing } from './user-listing'; import { CommunityLink } from './community-link'; +import { PictrsImage } from './pictrs-image'; import { md, mdToHtml, @@ -32,7 +33,6 @@ import { isImage, isVideo, getUnixTime, - pictrsImage, setupTippy, hostname, previewLines, @@ -164,27 +164,26 @@ export class PostListing extends Component<PostListingProps, PostListingState> { 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; } @@ -200,7 +199,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { 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> @@ -215,7 +214,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { 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> @@ -442,7 +441,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { class="pointer" onClick={linkEvent(this, this.handleImageExpandClick)} > - <img class="img-fluid img-expanded" src={this.getImage()} /> + <PictrsImage src={this.getImageSrc()} /> </span> </div> </span> diff --git a/src/shared/components/user-listing.tsx b/src/shared/components/user-listing.tsx index fd296fa..1b97dbb 100644 --- a/src/shared/components/user-listing.tsx +++ b/src/shared/components/user-listing.tsx @@ -1,13 +1,9 @@ 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; @@ -59,11 +55,7 @@ export class UserListing extends Component<UserListingProps, any> { 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> diff --git a/src/shared/utils.ts b/src/shared/utils.ts index 5b14f0d..9bc9b23 100644 --- a/src/shared/utils.ts +++ b/src/shared/utils.ts @@ -491,15 +491,6 @@ export function objectFlip(obj: any) { 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) || @@ -520,23 +511,6 @@ export function isCakeDay(published: string): boolean { ); } -// 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 { @@ -1090,23 +1064,6 @@ export function hostname(url: string): string { 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;