mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-23 03:22:19 +03:00
UBER-373: Fix blurry avatars and other images (#3353)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
4f2a079ab0
commit
6a7fe71447
@ -1,6 +1,6 @@
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import { getFileUrl } from '@hcengineering/presentation'
|
||||
import { Action, Menu, getEventPositionElement, showPopup } from '@hcengineering/ui'
|
||||
import { Action, IconSize, Menu, getEventPositionElement, getIconSize2x, showPopup } from '@hcengineering/ui'
|
||||
import { Node, createNodeFromContent, mergeAttributes, nodeInputRule } from '@tiptap/core'
|
||||
import { Plugin, PluginKey } from 'prosemirror-state'
|
||||
import plugin from '../plugin'
|
||||
@ -88,6 +88,27 @@ export const ImageRef = Node.create<ImageOptions>({
|
||||
HTMLAttributes
|
||||
)
|
||||
merged.src = getFileUrl(merged['file-id'], 'full')
|
||||
let width: IconSize | undefined
|
||||
switch (merged.width) {
|
||||
case '32px':
|
||||
width = 'small'
|
||||
break
|
||||
case '64px':
|
||||
width = 'medium'
|
||||
break
|
||||
case '128px':
|
||||
case '256px':
|
||||
width = 'large'
|
||||
break
|
||||
case '512px':
|
||||
width = 'x-large'
|
||||
break
|
||||
}
|
||||
if (width !== undefined) {
|
||||
merged.src = getFileUrl(merged['file-id'], width)
|
||||
merged.srcset =
|
||||
getFileUrl(merged['file-id'], width) + ' 1x,' + getFileUrl(merged['file-id'], getIconSize2x(width)) + ' 2x'
|
||||
}
|
||||
merged.class = 'textEditorImage'
|
||||
return ['img', merged]
|
||||
},
|
||||
|
@ -189,6 +189,7 @@ export type TooltipAlignment = 'top' | 'bottom' | 'left' | 'right'
|
||||
export type VerticalAlignment = 'top' | 'bottom'
|
||||
export type HorizontalAlignment = 'left' | 'right'
|
||||
|
||||
// Be aware to update front getResizeID() to properly store resized images.
|
||||
export type IconSize =
|
||||
| 'inline'
|
||||
| 'tiny'
|
||||
@ -202,6 +203,26 @@ export type IconSize =
|
||||
| '2x-large'
|
||||
| 'full'
|
||||
|
||||
export function getIconSize2x (size: IconSize): IconSize {
|
||||
switch (size) {
|
||||
case 'inline':
|
||||
case 'tiny':
|
||||
case 'x-small':
|
||||
case 'small':
|
||||
case 'card':
|
||||
case 'smaller':
|
||||
case 'medium':
|
||||
return 'large'
|
||||
case 'large':
|
||||
return 'x-large'
|
||||
case 'x-large':
|
||||
return '2x-large'
|
||||
case '2x-large':
|
||||
case 'full':
|
||||
return 'full'
|
||||
}
|
||||
}
|
||||
|
||||
export interface DateOrShift {
|
||||
date?: number
|
||||
shift?: number
|
||||
|
@ -16,7 +16,7 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import type { Attachment } from '@hcengineering/attachment'
|
||||
import { showPopup, closeTooltip, Label } from '@hcengineering/ui'
|
||||
import { showPopup, closeTooltip, Label, getIconSize2x } from '@hcengineering/ui'
|
||||
import presentation, { PDFViewer, getFileUrl } from '@hcengineering/presentation'
|
||||
import filesize from 'filesize'
|
||||
|
||||
@ -69,6 +69,18 @@
|
||||
}
|
||||
|
||||
let download: HTMLAnchorElement
|
||||
|
||||
$: imgStyle = isImage(value.type)
|
||||
? `background-image: url(${getFileUrl(value.file, 'large')});
|
||||
background-image: -webkit-image-set(
|
||||
${getFileUrl(value.file, 'large')} 1x,
|
||||
${getFileUrl(value.file, getIconSize2x('large'))} 2x
|
||||
);
|
||||
background-image: image-set(
|
||||
${getFileUrl(value.file, 'large')} 1x,
|
||||
${getFileUrl(value.file, getIconSize2x('large'))} 2x
|
||||
);`
|
||||
: ''
|
||||
</script>
|
||||
|
||||
<div class="flex-row-center attachment-container">
|
||||
@ -83,7 +95,7 @@
|
||||
class="flex-center icon"
|
||||
class:svg={value.type === 'image/svg+xml'}
|
||||
class:image={isImage(value.type)}
|
||||
style:background-image={isImage(value.type) ? `url(${getFileUrl(value.file, 'large')})` : 'none'}
|
||||
style={imgStyle}
|
||||
>
|
||||
{#if !isImage(value.type)}{iconLabel(value.name)}{/if}
|
||||
</div> -->
|
||||
|
@ -16,7 +16,7 @@
|
||||
<script lang="ts">
|
||||
import type { Attachment } from '@hcengineering/attachment'
|
||||
import { getFileUrl, PDFViewer } from '@hcengineering/presentation'
|
||||
import { showPopup, closeTooltip } from '@hcengineering/ui'
|
||||
import { showPopup, closeTooltip, getIconSize2x } from '@hcengineering/ui'
|
||||
import { getType } from '../utils'
|
||||
import AttachmentPresenter from './AttachmentPresenter.svelte'
|
||||
import AttachmentActions from './AttachmentActions.svelte'
|
||||
@ -47,7 +47,15 @@
|
||||
dispatch('open', popupInfo.id)
|
||||
}}
|
||||
>
|
||||
<img src={getFileUrl(value.file, 'large')} alt={value.name} />
|
||||
<img
|
||||
src={getFileUrl(value.file, 'large')}
|
||||
srcset={`${getFileUrl(value.file, 'large', value.name)} 1x, ${getFileUrl(
|
||||
value.file,
|
||||
getIconSize2x('large'),
|
||||
value.name
|
||||
)} 2x`}
|
||||
alt={value.name}
|
||||
/>
|
||||
<div class="actions conner">
|
||||
<AttachmentActions attachment={value} {isSaved} />
|
||||
</div>
|
||||
|
@ -40,13 +40,13 @@
|
||||
export let size: IconSize
|
||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||
|
||||
let url: string | undefined
|
||||
let url: string[] | undefined
|
||||
let avatarProvider: AvatarProvider | undefined
|
||||
|
||||
async function update (size: IconSize, avatar?: string | null, direct?: Blob) {
|
||||
if (direct !== undefined) {
|
||||
getBlobURL(direct).then((blobURL) => {
|
||||
url = blobURL
|
||||
url = [blobURL]
|
||||
avatarProvider = undefined
|
||||
})
|
||||
} else if (avatar) {
|
||||
@ -69,14 +69,16 @@
|
||||
$: update(size, avatar, direct)
|
||||
|
||||
let imageElement: HTMLImageElement | undefined = undefined
|
||||
|
||||
$: srcset = url?.slice(1)?.join(', ')
|
||||
</script>
|
||||
|
||||
<div class="ava-{size} flex-center avatar-container" class:no-img={!url}>
|
||||
{#if url}
|
||||
{#if size === 'large' || size === 'x-large' || size === '2x-large'}
|
||||
<img class="ava-{size} ava-blur" src={url} alt={''} bind:this={imageElement} />
|
||||
<img class="ava-{size} ava-blur" src={url[0]} {srcset} alt={''} bind:this={imageElement} />
|
||||
{/if}
|
||||
<img class="ava-{size} ava-mask" src={url} alt={''} bind:this={imageElement} />
|
||||
<img class="ava-{size} ava-mask" src={url[0]} {srcset} alt={''} bind:this={imageElement} />
|
||||
{:else}
|
||||
<Icon icon={icon ?? AvatarIcon} size={size === 'card' ? 'x-small' : size} />
|
||||
{/if}
|
||||
|
@ -19,12 +19,22 @@ import { Class, Client, DocumentQuery, Ref, RelatedDocument, WithLookup } from '
|
||||
import login from '@hcengineering/login'
|
||||
import { IntlString, Resources, getResource } from '@hcengineering/platform'
|
||||
import { MessageBox, ObjectSearchResult, getClient, getFileUrl } from '@hcengineering/presentation'
|
||||
import { AnyComponent, AnySvelteComponent, TooltipAlignment, parseURL, showPopup } from '@hcengineering/ui'
|
||||
import {
|
||||
AnyComponent,
|
||||
AnySvelteComponent,
|
||||
IconSize,
|
||||
TooltipAlignment,
|
||||
getIconSize2x,
|
||||
parseURL,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import AccountArrayEditor from './components/AccountArrayEditor.svelte'
|
||||
import AccountBox from './components/AccountBox.svelte'
|
||||
import AssigneeBox from './components/AssigneeBox.svelte'
|
||||
import Avatar from './components/Avatar.svelte'
|
||||
import ChannelFilter from './components/ChannelFilter.svelte'
|
||||
import ChannelPanel from './components/ChannelPanel.svelte'
|
||||
import ChannelPresenter from './components/ChannelPresenter.svelte'
|
||||
import Channels from './components/Channels.svelte'
|
||||
import ChannelsDropdown from './components/ChannelsDropdown.svelte'
|
||||
import ChannelsEditor from './components/ChannelsEditor.svelte'
|
||||
@ -39,17 +49,21 @@ import ContactsTabs from './components/ContactsTabs.svelte'
|
||||
import CreateEmployee from './components/CreateEmployee.svelte'
|
||||
import CreateOrganization from './components/CreateOrganization.svelte'
|
||||
import CreatePerson from './components/CreatePerson.svelte'
|
||||
import DeleteConfirmationPopup from './components/DeleteConfirmationPopup.svelte'
|
||||
import EditEmployee from './components/EditEmployee.svelte'
|
||||
import EditMember from './components/EditMember.svelte'
|
||||
import EditOrganization from './components/EditOrganization.svelte'
|
||||
import EditPerson from './components/EditPerson.svelte'
|
||||
import EditableAvatar from './components/EditableAvatar.svelte'
|
||||
import EmployeeAccountFilterValuePresenter from './components/EmployeeAccountFilterValuePresenter.svelte'
|
||||
import EmployeeAccountPresenter from './components/EmployeeAccountPresenter.svelte'
|
||||
import EmployeeAccountRefPresenter from './components/EmployeeAccountRefPresenter.svelte'
|
||||
import EmployeeArrayEditor from './components/EmployeeArrayEditor.svelte'
|
||||
import EmployeeBox from './components/EmployeeBox.svelte'
|
||||
import EmployeeBrowser from './components/EmployeeBrowser.svelte'
|
||||
import EmployeeEditor from './components/EmployeeEditor.svelte'
|
||||
import EmployeeFilter from './components/EmployeeFilter.svelte'
|
||||
import EmployeeFilterValuePresenter from './components/EmployeeFilterValuePresenter.svelte'
|
||||
import EmployeePresenter from './components/EmployeePresenter.svelte'
|
||||
import EmployeeRefPresenter from './components/EmployeeRefPresenter.svelte'
|
||||
import MemberPresenter from './components/MemberPresenter.svelte'
|
||||
@ -61,24 +75,18 @@ import OrganizationPresenter from './components/OrganizationPresenter.svelte'
|
||||
import PersonEditor from './components/PersonEditor.svelte'
|
||||
import PersonPresenter from './components/PersonPresenter.svelte'
|
||||
import PersonRefPresenter from './components/PersonRefPresenter.svelte'
|
||||
import SelectAvatars from './components/SelectAvatars.svelte'
|
||||
import SocialEditor from './components/SocialEditor.svelte'
|
||||
import SpaceMembers from './components/SpaceMembers.svelte'
|
||||
import UserBox from './components/UserBox.svelte'
|
||||
import UserBoxItems from './components/UserBoxItems.svelte'
|
||||
import UserBoxList from './components/UserBoxList.svelte'
|
||||
import UserInfo from './components/UserInfo.svelte'
|
||||
import UsersPopup from './components/UsersPopup.svelte'
|
||||
import ActivityChannelMessage from './components/activity/ActivityChannelMessage.svelte'
|
||||
import ActivityChannelPresenter from './components/activity/ActivityChannelPresenter.svelte'
|
||||
import ExpandRightDouble from './components/icons/ExpandRightDouble.svelte'
|
||||
import IconMembers from './components/icons/Members.svelte'
|
||||
import ChannelPresenter from './components/ChannelPresenter.svelte'
|
||||
import ChannelPanel from './components/ChannelPanel.svelte'
|
||||
import ActivityChannelPresenter from './components/activity/ActivityChannelPresenter.svelte'
|
||||
import SelectAvatars from './components/SelectAvatars.svelte'
|
||||
import UserBoxItems from './components/UserBoxItems.svelte'
|
||||
import EmployeeFilter from './components/EmployeeFilter.svelte'
|
||||
import EmployeeFilterValuePresenter from './components/EmployeeFilterValuePresenter.svelte'
|
||||
import EmployeeAccountFilterValuePresenter from './components/EmployeeAccountFilterValuePresenter.svelte'
|
||||
import DeleteConfirmationPopup from './components/DeleteConfirmationPopup.svelte'
|
||||
|
||||
import contact from './plugin'
|
||||
import {
|
||||
@ -98,6 +106,7 @@ import {
|
||||
resolveLocation
|
||||
} from './utils'
|
||||
|
||||
export * from './utils'
|
||||
export { employeeByIdStore, employeesStore } from './utils'
|
||||
export {
|
||||
Channels,
|
||||
@ -254,8 +263,6 @@ export interface PersonLabelTooltip {
|
||||
props?: any
|
||||
}
|
||||
|
||||
export * from './utils'
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
actionImpl: {
|
||||
KickEmployee: kickEmployee,
|
||||
@ -321,9 +328,19 @@ export default async (): Promise<Resources> => ({
|
||||
) => await queryContact(contact.class.Organization, client, query, filter)
|
||||
},
|
||||
function: {
|
||||
GetFileUrl: getFileUrl,
|
||||
GetGravatarUrl: getGravatarUrl,
|
||||
GetColorUrl: (uri: string) => uri,
|
||||
GetFileUrl: (file: string, size: IconSize, fileName?: string) => {
|
||||
return [
|
||||
getFileUrl(file, size, fileName),
|
||||
getFileUrl(file, size, fileName) + ' 1x',
|
||||
getFileUrl(file, getIconSize2x(size), fileName) + ' 2x'
|
||||
]
|
||||
},
|
||||
GetGravatarUrl: (file: string, size: IconSize, fileName?: string) => [
|
||||
getGravatarUrl(file, size),
|
||||
getGravatarUrl(file, size) + ' 1x',
|
||||
getGravatarUrl(file, getIconSize2x(size)) + ' 2x'
|
||||
],
|
||||
GetColorUrl: (uri: string) => [uri],
|
||||
EmployeeSort: employeeSort,
|
||||
FilterChannelInResult: filterChannelInResult,
|
||||
FilterChannelNinResult: filterChannelNinResult,
|
||||
|
@ -81,7 +81,7 @@ export enum AvatarType {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type GetAvatarUrl = (uri: string, size: IconSize) => string
|
||||
export type GetAvatarUrl = (uri: string, size: IconSize) => string[]
|
||||
|
||||
/**
|
||||
* @public
|
||||
|
@ -54,7 +54,7 @@ export function getGravatarUrl (
|
||||
case 'x-small':
|
||||
case 'small':
|
||||
case 'medium':
|
||||
width = 64
|
||||
width = 128
|
||||
break
|
||||
case 'large':
|
||||
width = 256
|
||||
@ -62,6 +62,12 @@ export function getGravatarUrl (
|
||||
case 'x-large':
|
||||
width = 512
|
||||
break
|
||||
case '2x-large':
|
||||
width = 1024
|
||||
break
|
||||
case 'full':
|
||||
width = 2048
|
||||
break
|
||||
}
|
||||
return `https://gravatar.com/avatar/${gravatarId}?s=${width}&d=${placeholder}`
|
||||
}
|
||||
|
@ -101,7 +101,7 @@
|
||||
}
|
||||
}
|
||||
.back {
|
||||
// background: url('../../img/back_signin.png');
|
||||
background-image: url('../../img/login_back.png');
|
||||
background-image: -webkit-image-set(
|
||||
'../../img/login_back.avif' 1x,
|
||||
'../../img/login_back_2x.avif' 2x,
|
||||
|
@ -491,6 +491,19 @@ export function start (
|
||||
server.close()
|
||||
}
|
||||
}
|
||||
|
||||
// export type IconSize =
|
||||
// | 'inline'
|
||||
// | 'tiny'
|
||||
// | 'card'
|
||||
// | 'x-small'
|
||||
// | 'smaller'
|
||||
// | 'small'
|
||||
// | 'medium'
|
||||
// | 'large'
|
||||
// | 'x-large'
|
||||
// | '2x-large'
|
||||
// | 'full'
|
||||
async function getResizeID (
|
||||
size: string,
|
||||
uuid: string,
|
||||
@ -502,7 +515,9 @@ async function getResizeID (
|
||||
switch (size) {
|
||||
case 'inline':
|
||||
case 'tiny':
|
||||
case 'card':
|
||||
case 'x-small':
|
||||
case 'smaller':
|
||||
case 'small':
|
||||
case 'medium':
|
||||
width = 64
|
||||
@ -513,6 +528,10 @@ async function getResizeID (
|
||||
case 'x-large':
|
||||
width = 512
|
||||
break
|
||||
case '2x-large':
|
||||
size = '2x-large_v2'
|
||||
width = 1024
|
||||
break
|
||||
}
|
||||
let hasSmall = false
|
||||
const sizeId = uuid + `%size%${width}`
|
||||
|
Loading…
Reference in New Issue
Block a user