mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-12-23 11:21:26 +00:00
Making front end work w/ pictrs v2. Fixes #57
This commit is contained in:
parent
7cb650dbd3
commit
40e1aca891
8 changed files with 95 additions and 92 deletions
|
@ -297,6 +297,10 @@ br.big {
|
||||||
margin-top: -60px;
|
margin-top: -60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.img-icon {
|
||||||
|
width: 2rem; height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.tribute-container ul {
|
.tribute-container ul {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Component } from 'inferno';
|
import { Component } from 'inferno';
|
||||||
|
import { PictrsImage } from './pictrs-image';
|
||||||
|
|
||||||
interface BannerIconHeaderProps {
|
interface BannerIconHeaderProps {
|
||||||
banner?: string;
|
banner?: string;
|
||||||
|
@ -13,15 +14,12 @@ export class BannerIconHeader extends Component<BannerIconHeaderProps, any> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="position-relative mb-2">
|
<div class="position-relative mb-2">
|
||||||
{this.props.banner && (
|
{this.props.banner && <PictrsImage src={this.props.banner} />}
|
||||||
<img src={this.props.banner} class="banner img-fluid" />
|
|
||||||
)}
|
|
||||||
{this.props.icon && (
|
{this.props.icon && (
|
||||||
<img
|
<PictrsImage
|
||||||
src={this.props.icon}
|
src={this.props.icon}
|
||||||
className={`ml-2 mb-0 ${
|
iconOverlay
|
||||||
this.props.banner ? 'avatar-pushup' : ''
|
pushup={!!this.props.banner}
|
||||||
} rounded-circle avatar-overlay`}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { Component } from 'inferno';
|
import { Component } from 'inferno';
|
||||||
import { Link } from 'inferno-router';
|
import { Link } from 'inferno-router';
|
||||||
import { Community } from 'lemmy-js-client';
|
import { Community } from 'lemmy-js-client';
|
||||||
import { hostname, pictrsAvatarThumbnail, showAvatars } from '../utils';
|
import { hostname, showAvatars } from '../utils';
|
||||||
|
import { PictrsImage } from './pictrs-image';
|
||||||
|
|
||||||
interface CommunityOther {
|
interface CommunityOther {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -47,11 +48,7 @@ export class CommunityLink extends Component<CommunityLinkProps, any> {
|
||||||
to={link}
|
to={link}
|
||||||
>
|
>
|
||||||
{!this.props.hideAvatar && community.icon && showAvatars() && (
|
{!this.props.hideAvatar && community.icon && showAvatars() && (
|
||||||
<img
|
<PictrsImage src={community.icon} icon />
|
||||||
style="width: 2rem; height: 2rem;"
|
|
||||||
src={pictrsAvatarThumbnail(community.icon)}
|
|
||||||
class="rounded-circle mr-2"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<span>{displayName}</span>
|
<span>{displayName}</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -20,7 +20,6 @@ import {
|
||||||
} from 'lemmy-js-client';
|
} from 'lemmy-js-client';
|
||||||
import {
|
import {
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
pictrsAvatarThumbnail,
|
|
||||||
showAvatars,
|
showAvatars,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
toast,
|
toast,
|
||||||
|
@ -32,6 +31,7 @@ import {
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
|
import { PictrsImage } from './pictrs-image';
|
||||||
|
|
||||||
interface NavbarProps {
|
interface NavbarProps {
|
||||||
site: GetSiteResponse;
|
site: GetSiteResponse;
|
||||||
|
@ -190,12 +190,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
to="/"
|
to="/"
|
||||||
>
|
>
|
||||||
{this.props.site.site.icon && showAvatars() && (
|
{this.props.site.site.icon && showAvatars() && (
|
||||||
<img
|
<PictrsImage src={this.props.site.site.icon} icon />
|
||||||
src={pictrsAvatarThumbnail(this.props.site.site.icon)}
|
|
||||||
height="32"
|
|
||||||
width="32"
|
|
||||||
class="rounded-circle mr-2"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{this.props.site.site.name}
|
{this.props.site.site.name}
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -345,12 +340,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{user.avatar && showAvatars() && (
|
{user.avatar && showAvatars() && (
|
||||||
<img
|
<PictrsImage src={user.avatar} icon />
|
||||||
src={pictrsAvatarThumbnail(user.avatar)}
|
|
||||||
height="32"
|
|
||||||
width="32"
|
|
||||||
class="rounded-circle mr-2"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{user.preferred_username
|
{user.preferred_username
|
||||||
? user.preferred_username
|
? user.preferred_username
|
||||||
|
|
66
src/shared/components/pictrs-image.tsx
Normal file
66
src/shared/components/pictrs-image.tsx
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import { PostForm } from './post-form';
|
||||||
import { IFramelyCard } from './iframely-card';
|
import { IFramelyCard } from './iframely-card';
|
||||||
import { UserListing } from './user-listing';
|
import { UserListing } from './user-listing';
|
||||||
import { CommunityLink } from './community-link';
|
import { CommunityLink } from './community-link';
|
||||||
|
import { PictrsImage } from './pictrs-image';
|
||||||
import {
|
import {
|
||||||
md,
|
md,
|
||||||
mdToHtml,
|
mdToHtml,
|
||||||
|
@ -32,7 +33,6 @@ import {
|
||||||
isImage,
|
isImage,
|
||||||
isVideo,
|
isVideo,
|
||||||
getUnixTime,
|
getUnixTime,
|
||||||
pictrsImage,
|
|
||||||
setupTippy,
|
setupTippy,
|
||||||
hostname,
|
hostname,
|
||||||
previewLines,
|
previewLines,
|
||||||
|
@ -164,27 +164,26 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
imgThumb(src: string) {
|
imgThumb(src: string) {
|
||||||
let post = this.props.post;
|
let post = this.props.post;
|
||||||
return (
|
return (
|
||||||
<img
|
<PictrsImage
|
||||||
className={`img-fluid thumbnail rounded ${
|
|
||||||
post.nsfw || post.community_nsfw ? 'img-blur' : ''
|
|
||||||
}`}
|
|
||||||
src={src}
|
src={src}
|
||||||
|
thumbnail
|
||||||
|
nsfw={post.nsfw || post.community_nsfw}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getImage(thumbnail: boolean = false) {
|
getImageSrc(): string {
|
||||||
let post = this.props.post;
|
let post = this.props.post;
|
||||||
if (isImage(post.url)) {
|
if (isImage(post.url)) {
|
||||||
if (post.url.includes('pictrs')) {
|
if (post.url.includes('pictrs')) {
|
||||||
return pictrsImage(post.url, thumbnail);
|
return post.url;
|
||||||
} else if (post.thumbnail_url) {
|
} else if (post.thumbnail_url) {
|
||||||
return pictrsImage(post.thumbnail_url, thumbnail);
|
return post.thumbnail_url;
|
||||||
} else {
|
} else {
|
||||||
return post.url;
|
return post.url;
|
||||||
}
|
}
|
||||||
} else if (post.thumbnail_url) {
|
} else if (post.thumbnail_url) {
|
||||||
return pictrsImage(post.thumbnail_url, thumbnail);
|
return post.thumbnail_url;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -200,7 +199,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
data-tippy-content={i18n.t('expand_here')}
|
data-tippy-content={i18n.t('expand_here')}
|
||||||
onClick={linkEvent(this, this.handleImageExpandClick)}
|
onClick={linkEvent(this, this.handleImageExpandClick)}
|
||||||
>
|
>
|
||||||
{this.imgThumb(this.getImage(true))}
|
{this.imgThumb(this.getImageSrc())}
|
||||||
<svg class="icon mini-overlay">
|
<svg class="icon mini-overlay">
|
||||||
<use xlinkHref="#icon-image"></use>
|
<use xlinkHref="#icon-image"></use>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -215,7 +214,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
rel="noopener"
|
rel="noopener"
|
||||||
title={post.url}
|
title={post.url}
|
||||||
>
|
>
|
||||||
{this.imgThumb(this.getImage(true))}
|
{this.imgThumb(this.getImageSrc())}
|
||||||
<svg class="icon mini-overlay">
|
<svg class="icon mini-overlay">
|
||||||
<use xlinkHref="#icon-external-link"></use>
|
<use xlinkHref="#icon-external-link"></use>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -442,7 +441,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||||
class="pointer"
|
class="pointer"
|
||||||
onClick={linkEvent(this, this.handleImageExpandClick)}
|
onClick={linkEvent(this, this.handleImageExpandClick)}
|
||||||
>
|
>
|
||||||
<img class="img-fluid img-expanded" src={this.getImage()} />
|
<PictrsImage src={this.getImageSrc()} />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
import { Component } from 'inferno';
|
import { Component } from 'inferno';
|
||||||
import { Link } from 'inferno-router';
|
import { Link } from 'inferno-router';
|
||||||
import { UserView } from 'lemmy-js-client';
|
import { UserView } from 'lemmy-js-client';
|
||||||
import {
|
import { showAvatars, hostname, isCakeDay } from '../utils';
|
||||||
pictrsAvatarThumbnail,
|
|
||||||
showAvatars,
|
|
||||||
hostname,
|
|
||||||
isCakeDay,
|
|
||||||
} from '../utils';
|
|
||||||
import { CakeDay } from './cake-day';
|
import { CakeDay } from './cake-day';
|
||||||
|
import { PictrsImage } from './pictrs-image';
|
||||||
|
|
||||||
export interface UserOther {
|
export interface UserOther {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -59,11 +55,7 @@ export class UserListing extends Component<UserListingProps, any> {
|
||||||
to={link}
|
to={link}
|
||||||
>
|
>
|
||||||
{!this.props.hideAvatar && user.avatar && showAvatars() && (
|
{!this.props.hideAvatar && user.avatar && showAvatars() && (
|
||||||
<img
|
<PictrsImage src={user.avatar} icon />
|
||||||
style="width: 2rem; height: 2rem;"
|
|
||||||
src={pictrsAvatarThumbnail(user.avatar)}
|
|
||||||
class="rounded-circle mr-2"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<span>{displayName}</span>
|
<span>{displayName}</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -491,15 +491,6 @@ export function objectFlip(obj: any) {
|
||||||
return ret;
|
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 {
|
export function showAvatars(): boolean {
|
||||||
return (
|
return (
|
||||||
(UserService.Instance.user && UserService.Instance.user.show_avatars) ||
|
(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(
|
export function isCommentType(
|
||||||
item: Comment | PrivateMessage | Post
|
item: Comment | PrivateMessage | Post
|
||||||
): item is Comment {
|
): item is Comment {
|
||||||
|
@ -1090,23 +1064,6 @@ export function hostname(url: string): string {
|
||||||
return cUrl.port ? `${cUrl.hostname}:${cUrl.port}` : `${cUrl.hostname}`;
|
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 {
|
export function validTitle(title?: string): boolean {
|
||||||
// Initial title is null, minimum length is taken care of by textarea's minLength={3}
|
// Initial title is null, minimum length is taken care of by textarea's minLength={3}
|
||||||
if (title === null || title.length < 3) return true;
|
if (title === null || title.length < 3) return true;
|
||||||
|
|
Loading…
Reference in a new issue