Merge branch 'develop' into bugfix/fix-desktop-navigation

This commit is contained in:
Alexey Safronov 2023-02-14 18:38:22 +03:00
commit 3693d7ee7e
28 changed files with 402 additions and 83 deletions

View File

@ -244,17 +244,17 @@ public class EmployeeFullDtoHelper : EmployeeDtoHelper
if (_context.Check("avatarMax"))
{
result.AvatarMax = await _userPhotoManager.GetMaxPhotoURL(userInfo.Id) + $"?_={cacheKey}";
result.AvatarMax = await _userPhotoManager.GetMaxPhotoURL(userInfo.Id) + $"?hash={cacheKey}";
}
if (_context.Check("avatarMedium"))
{
result.AvatarMedium = await _userPhotoManager.GetMediumPhotoURL(userInfo.Id) + $"?_={cacheKey}";
result.AvatarMedium = await _userPhotoManager.GetMediumPhotoURL(userInfo.Id) + $"?hash={cacheKey}";
}
if (_context.Check("avatar"))
{
result.Avatar = await _userPhotoManager.GetBigPhotoURL(userInfo.Id) + $"?_={cacheKey}";
result.Avatar = await _userPhotoManager.GetBigPhotoURL(userInfo.Id) + $"?hash={cacheKey}";
}
if (_context.Check("listAdminModules"))

View File

@ -73,7 +73,7 @@
"DeleteTheme": "Delete theme",
"DeleteThemeForever": "Delete theme forever?",
"DeleteThemeNotice": "The theme will be deleted permanently. You will not be able to undo this action.",
"DeveloperTools": "Developer",
"DeveloperTools": "Developer Tools",
"Disabled": "Disabled",
"DownloadCopy": "Download copy",
"DownloadReportBtnText": "Download report",

View File

@ -73,7 +73,7 @@
"DeleteTheme": "Удалить тему",
"DeleteThemeForever": "Удалить тему навсегда?",
"DeleteThemeNotice": "Тема будет удалена навсегда. Вы не сможете отменить это действие.",
"DeveloperTools": "Разработчик",
"DeveloperTools": "Инструменты разработчика",
"Disabled": "Отключено",
"DownloadCopy": "Скачать копию",
"DownloadReportBtn": "Скачать и открыть отчет",

View File

@ -212,10 +212,6 @@ const paddingCss = css`
@media ${desktop} {
padding-right: 3px;
}
@media ${tablet} {
margin-left: -1px;
}
`;
const StyledGridWrapper = styled.div`

View File

@ -15,7 +15,7 @@ const StyledInfoPanelBody = styled.div`
: css`
padding: 80px 3px 0 20px;
@media ${hugeMobile} {
padding: 80px 8px 0 16px;
padding: 80px 0 0 16px;
}
`}

View File

@ -1,5 +1,5 @@
import styled from "styled-components";
import { isMobileOnly } from "react-device-detect";
import { Base } from "@docspace/components/themes";
const StyledThumbnail = styled.div`
@ -7,13 +7,14 @@ const StyledThumbnail = styled.div`
justify-content: center;
align-items: center;
width: 100%;
height: auto;
height: ${isMobileOnly ? "188" : "240"}px;
img {
border: ${(props) => `solid 1px ${props.theme.infoPanel.borderColor}`};
border-radius: 6px;
width: auto;
max-width: 100%;
height: auto;
width: 100%;
height: 100%;
object-fit: none;
object-position: top;
}
`;
@ -30,7 +31,8 @@ const StyledNoThumbnail = styled.div`
border-radius: 16px;
}
.custom-logo {
outline: 1px solid ${(props) => props.theme.infoPanel.details.customLogoBorderColor};
outline: 1px solid ${(props) =>
props.theme.infoPanel.details.customLogoBorderColor};
`;
const StyledAccess = styled.div`

View File

@ -1,11 +1,11 @@
import styled from "styled-components";
import { isMobileOnly } from "react-device-detect";
import { Base } from "@docspace/components/themes";
const StyledGalleryThumbnail = styled.div`
box-sizing: border-box;
width: 100%;
height: 346px;
height: ${isMobileOnly ? "335" : "346"}px;
overflow: hidden;
border: ${(props) =>
`solid 1px ${props.theme.infoPanel.gallery.borderColor}`};

View File

@ -27,6 +27,7 @@ const FilesMediaViewer = (props) => {
setToPreviewFile,
setScrollToItem,
setCurrentId,
setAlreadyFetchingRooms,
setBufferSelection,
isFavoritesFolder,
archiveRoomsId,
@ -72,6 +73,7 @@ const FilesMediaViewer = (props) => {
fetchFiles(previewFile.folderId).finally(() => {
setIsLoading(false);
setFirstLoad(false);
setAlreadyFetchingRooms(false);
});
}
}, [previewFile]);
@ -230,6 +232,7 @@ export default inject(
isPreview,
resetUrl,
setSelection,
setAlreadyFetchingRooms,
} = filesStore;
const {
visible,
@ -286,6 +289,7 @@ export default inject(
setScrollToItem,
setCurrentId,
setBufferSelection,
setAlreadyFetchingRooms,
isFavoritesFolder,
onClickFavorite,
onClickDownloadAs,

View File

@ -261,7 +261,7 @@ const StyledFileTileTop = styled.div`
position: absolute;
height: 100%;
width: 100%;
object-fit: ${(props) => (props.isMedia ? "cover" : "none")};
object-fit: none;
object-position: top;
z-index: 0;
border-radius: 6px 6px 0 0;

View File

@ -55,6 +55,7 @@ let timeout = null,
CancelToken,
source;
const backUrl = window.location.origin;
const PriceCalculation = ({
t,
user,
@ -71,13 +72,19 @@ const PriceCalculation = ({
currencySymbol,
isAlreadyPaid,
isFreeAfterPaidPeriod,
setStartPaymentLink,
managersCount,
}) => {
useEffect(() => {
useEffect(async () => {
initializeInfo();
!isAlreadyPaid && setStartPaymentLink(t);
if (isAlreadyPaid) return;
try {
const link = await getPaymentLink(managersCount, source?.token, backUrl);
setPaymentLink(link);
} catch (e) {
toastr.error(t("ErrorNotification"));
}
return () => {
timeout && clearTimeout(timeout);
@ -103,7 +110,7 @@ const PriceCalculation = ({
CancelToken = axios.CancelToken;
source = CancelToken.source();
await getPaymentLink(value, source.token)
await getPaymentLink(value, source.token, backUrl)
.then((link) => {
setPaymentLink(link);
setIsLoading(false);
@ -207,7 +214,6 @@ export default inject(({ auth, payments }) => {
setManagersCount,
maxAvailableManagersCount,
initializeInfo,
setStartPaymentLink,
managersCount,
} = payments;
const { theme } = auth.settingsStore;
@ -224,7 +230,7 @@ export default inject(({ auth, payments }) => {
return {
managersCount,
setStartPaymentLink,
isFreeTariff,
setManagersCount,
tariffsInfo,

View File

@ -140,15 +140,6 @@ class PaymentStore {
}
};
setStartPaymentLink = async (t) => {
try {
const link = await api.portal.getPaymentLink(this.managersCount);
this.setPaymentLink(link);
} catch (e) {
toastr.error(t("ErrorNotification"));
}
};
setTotalPrice = (value) => {
const price = this.getTotalCostByFormula(value);
if (price !== this.totalPrice) this.totalPrice = price;

View File

@ -239,12 +239,13 @@ export function getPaymentAccount() {
return request({ method: "get", url: "/portal/payment/account" });
}
export function getPaymentLink(adminCount, cancelToken) {
export function getPaymentLink(adminCount, cancelToken, backUrl) {
return request({
method: "put",
url: `/portal/payment/url`,
data: {
quantity: { admin: adminCount },
backUrl,
},
cancelToken,
});

View File

@ -23,7 +23,7 @@ export interface MediaViewerProps {
playlistPos: number;
getIcon: (size: number, ext: string, ...arg: any) => any;
getIcon: (size: number, ext: string, ...arg: any) => string;
onClose: VoidFunction;
onError?: VoidFunction;

View File

@ -0,0 +1,112 @@
import React from "react";
import MediaZoomInIcon from "PUBLIC_DIR/images/media.zoomin.react.svg";
import MediaZoomOutIcon from "PUBLIC_DIR/images/media.zoomout.react.svg";
import MediaRotateLeftIcon from "PUBLIC_DIR/images/media.rotateleft.react.svg";
import MediaRotateRightIcon from "PUBLIC_DIR/images/media.rotateright.react.svg";
import MediaDeleteIcon from "PUBLIC_DIR/images/media.delete.react.svg";
import MediaDownloadIcon from "PUBLIC_DIR/images/download.react.svg";
import MediaFavoriteIcon from "PUBLIC_DIR/images/favorite.react.svg";
import ViewerSeparator from "PUBLIC_DIR/images/viewer.separator.react.svg";
export const getCustomToolbar = (
onDeleteClick: VoidFunction,
onDownloadClick: VoidFunction
) => {
return [
{
key: "zoomOut",
percent: true,
actionType: 2,
render: (
<div className="iconContainer zoomOut">
<MediaZoomOutIcon size="scale" />
</div>
),
},
{
key: "percent",
actionType: 999,
},
{
key: "zoomIn",
actionType: 1,
render: (
<div className="iconContainer zoomIn">
<MediaZoomInIcon size="scale" />
</div>
),
},
{
key: "rotateLeft",
actionType: 5,
render: (
<div className="iconContainer rotateLeft">
<MediaRotateLeftIcon size="scale" />
</div>
),
},
{
key: "rotateRight",
actionType: 6,
render: (
<div className="iconContainer rotateRight">
<MediaRotateRightIcon size="scale" />
</div>
),
},
{
key: "separator download-separator",
actionType: -1,
noHover: true,
render: (
<div className="separator" style={{ height: "16px" }}>
<ViewerSeparator size="scale" />
</div>
),
},
{
key: "download",
actionType: 102,
render: (
<div className="iconContainer download" style={{ height: "16px" }}>
<MediaDownloadIcon size="scale" />
</div>
),
onClick: onDownloadClick,
},
{
key: "context-separator",
actionType: -1,
noHover: true,
render: (
<div className="separator" style={{ height: "16px" }}>
<ViewerSeparator size="scale" />
</div>
),
},
{
key: "context-menu",
actionType: -1,
},
{
key: "delete",
actionType: 103,
render: (
<div className="iconContainer viewer-delete">
<MediaDeleteIcon size="scale" />
</div>
),
onClick: onDeleteClick,
},
{
key: "favorite",
actionType: 104,
render: (
<div className="iconContainer viewer-favorite">
<MediaFavoriteIcon size="scale" />
</div>
),
},
];
};

View File

@ -1,4 +1,9 @@
import { NullOrUndefined, PlaylistType } from "../types";
import {
ContextMenuModel,
NullOrUndefined,
PlaylistType,
SeparatorType,
} from "../types";
export const mediaTypes = Object.freeze({
audio: 1,
@ -57,3 +62,7 @@ export const findNearestIndex = (
}
return found;
};
export const isSeparator = (arg: ContextMenuModel): arg is SeparatorType => {
return arg?.isSeparator;
};

View File

@ -1,7 +1,7 @@
import { isMobileOnly } from "react-device-detect";
import React, { useState, useCallback, useMemo, useEffect } from "react";
import ImageViewer from "./sub-components/image-viewer";
import ViewerWrapper from "./sub-components/ViewerWrapper";
import { MediaViewerProps } from "./MediaViewer.props";
import { FileStatus } from "@docspace/common/constants";
@ -425,7 +425,7 @@ function MediaViewer({
return (
<>
{canOpen && (
<ImageViewer
<ViewerWrapper
userAccess={props.userAccess}
visible={props.visible}
title={title}

View File

@ -0,0 +1,6 @@
import styled from "styled-components";
import DropDown from "@docspace/components/drop-down";
export const StyledDropDown = styled(DropDown)`
background: #333;
`;

View File

@ -0,0 +1,16 @@
import styled from "styled-components";
import DropDownItem from "@docspace/components/drop-down-item";
export const StyledDropDownItem = styled(DropDownItem)`
color: #fff;
.drop-down-item_icon svg {
path {
fill: #fff !important;
}
}
&:hover {
background: #444;
}
`;

View File

@ -0,0 +1,33 @@
import { ContextMenuModel, PlaylistType } from "../../types";
interface ViewerWrapperProps {
userAccess: boolean;
visible: boolean;
title: string;
images: { src: string; alt: string }[];
inactive: boolean;
playlist: PlaylistType[];
playlistPos: number;
isFavorite: boolean;
isImage: boolean;
isAudio: boolean;
isVideo: boolean;
isPreviewFile: boolean;
archiveRoom: boolean;
errorTitle: string;
headerIcon: string;
audioIcon: string;
onClose: VoidFunction;
onPrevClick: VoidFunction;
onNextClick: VoidFunction;
onDeleteClick: VoidFunction;
onDownloadClick: VoidFunction;
onSetSelectionFile: VoidFunction;
contextModel: () => ContextMenuModel[];
}
export default ViewerWrapperProps;

View File

@ -0,0 +1,120 @@
import React, { useMemo, memo, useCallback } from "react";
import equal from "fast-deep-equal/react";
import { Viewer } from "@docspace/components/viewer";
import { isSeparator } from "../../helpers";
import { getCustomToolbar } from "../../helpers/getCustomToolbar";
import { ContextMenuModel } from "../../types";
import { StyledDropDown } from "../StyledDropDown";
import { StyledDropDownItem } from "../StyledDropDownItem";
import ViewerWrapperProps from "./ViewerWrapper.props";
const DefaultSpeedZoom = 0.25;
function ViewerWrapper(props: ViewerWrapperProps) {
const onClickContextItem = useCallback(
(item: ContextMenuModel) => {
if (isSeparator(item)) return;
item.onClick();
props.onClose();
},
[props.onClose]
);
const generateContextMenu = (
isOpen: boolean,
right: string,
bottom: string
) => {
const model = props.contextModel();
return (
<StyledDropDown
open={isOpen}
isDefaultMode={false}
directionY="top"
directionX="right"
fixedDirection={true}
withBackdrop={false}
manualY={(bottom || "63") + "px"}
manualX={(right || "-31") + "px"}
>
{model.map((item) => {
if (item.disabled) return;
const isItemSeparator = isSeparator(item);
return (
<StyledDropDownItem
className={`${item.isSeparator ? "is-separator" : ""}`}
key={item.key}
label={isItemSeparator ? undefined : item.label}
icon={!isItemSeparator && item.icon ? item.icon : ""}
onClick={() => onClickContextItem(item)}
/>
);
})}
</StyledDropDown>
);
};
const toolbars = useMemo(() => {
const {
onDeleteClick,
onDownloadClick,
playlist,
playlistPos,
userAccess,
} = props;
const customToolbar = getCustomToolbar(onDeleteClick, onDownloadClick);
const canShare = playlist[playlistPos].canShare;
const toolbars =
!canShare && userAccess
? customToolbar.filter(
(x) => x.key !== "share" && x.key !== "share-separator"
)
: customToolbar.filter((x) => x.key !== "delete");
return toolbars;
}, [
props.onDeleteClick,
props.onDownloadClick,
props.playlist,
props.playlistPos,
props.userAccess,
]);
return (
<Viewer
title={props.title}
images={props.images}
isAudio={props.isAudio}
isVideo={props.isVideo}
visible={props.visible}
isImage={props.isImage}
playlist={props.playlist}
inactive={props.inactive}
audioIcon={props.audioIcon}
zoomSpeed={DefaultSpeedZoom}
errorTitle={props.errorTitle}
headerIcon={props.headerIcon}
customToolbar={() => toolbars}
playlistPos={props.playlistPos}
archiveRoom={props.archiveRoom}
isPreviewFile={props.isPreviewFile}
onMaskClick={props.onClose}
onNextClick={props.onNextClick}
onPrevClick={props.onPrevClick}
contextModel={props.contextModel}
onDownloadClick={props.onDownloadClick}
generateContextMenu={generateContextMenu}
onSetSelectionFile={props.onSetSelectionFile}
/>
);
}
export default memo(ViewerWrapper, (prevProps, nextProps) =>
equal(prevProps, nextProps)
);

View File

@ -87,3 +87,21 @@ export interface IFile {
viewUrl: string;
webUrl: string;
}
export type ContextMenuType = {
id?: string;
key: string;
label: string;
icon: string;
disabled: boolean;
onClick: VoidFunction;
isSeparator?: undefined;
};
export type SeparatorType = {
key: string;
isSeparator: boolean;
disabled: boolean;
};
export type ContextMenuModel = ContextMenuType | SeparatorType;

View File

@ -80,12 +80,12 @@ export const Viewer = (props) => {
};
}
return () => document.removeEventListener("touchstart", onTouch);
}, [isPlay, isOpenContextMenu]);
}, [isPlay, isOpenContextMenu, isImage]);
function resetTimer() {
setPanelVisible(true);
clearTimeout(timer);
timer = setTimeout(() => setPanelVisible(false), 5000);
timer = setTimeout(() => setPanelVisible(false), 2500);
setImageTimer(timer);
}

View File

@ -240,6 +240,10 @@ const StyledMobileDetails = styled.div`
.title {
font-weight: 600;
margin-top: 6px;
width: calc(100% - 100px);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`;

View File

@ -907,7 +907,7 @@ export default function ViewerPlayer(props) {
}
if (isMobileOnly && videoRef.current && displayUI) {
clearTimeout(globalTimer);
setGlobalTimer(setTimeout(() => setPanelVisible(false), 5000));
setGlobalTimer(setTimeout(() => setPanelVisible(false), 2500));
}
}, [displayUI, isOpenContextMenu, state.isControlTouch, props.isPlay]);

View File

@ -170,9 +170,11 @@ public class FileDtoHelper : FileEntryDtoHelper
result.ThumbnailStatus = file.ThumbnailStatus;
var cacheKey = Math.Abs(result.Updated.GetHashCode());
if (file.ThumbnailStatus == Thumbnail.Created)
{
result.ThumbnailUrl = _commonLinkUtility.GetFullAbsolutePath(_filesLinkUtility.GetFileThumbnailUrl(file.Id, file.Version));
result.ThumbnailUrl = _commonLinkUtility.GetFullAbsolutePath(_filesLinkUtility.GetFileThumbnailUrl(file.Id, file.Version)) + $"&hash={cacheKey}";
}
}
catch (Exception)

View File

@ -177,10 +177,10 @@ public class RoomLogoManager
return new Logo
{
Original = await GetLogoPathAsync(id, SizeName.Original) + $"?_={cacheKey}",
Large = await GetLogoPathAsync(id, SizeName.Large) + $"?_={cacheKey}",
Medium = await GetLogoPathAsync(id, SizeName.Medium) + $"?_={cacheKey}",
Small = await GetLogoPathAsync(id, SizeName.Small) + $"?_={cacheKey}"
Original = await GetLogoPathAsync(id, SizeName.Original) + $"?hash={cacheKey}",
Large = await GetLogoPathAsync(id, SizeName.Large) + $"?hash={cacheKey}",
Medium = await GetLogoPathAsync(id, SizeName.Medium) + $"?hash={cacheKey}",
Small = await GetLogoPathAsync(id, SizeName.Small) + $"?hash={cacheKey}"
};
}

View File

@ -44,6 +44,7 @@ public class FilesChunkedUploadSessionHolder : CommonChunkedUploadSessionHolder
private async Task<T> InternalFinalizeAsync<T>(CommonChunkedUploadSession commonChunkedUploadSession)
{
var chunkedUploadSession = commonChunkedUploadSession as ChunkedUploadSession<T>;
chunkedUploadSession.BytesTotal = chunkedUploadSession.BytesUploaded;
var fileDao = GetFileDao<T>();
var file = await fileDao.FinalizeUploadSessionAsync(chunkedUploadSession);
return file.Id;

View File

@ -152,7 +152,7 @@ public class Builder<T>
catch (Exception exception)
{
_logger.ErrorBuildThumbnailsTenantId(fileData.TenantId, exception);
}
}
}
private async Task GenerateThumbnail(IFileDao<T> fileDao, FileData<T> fileData)
@ -188,6 +188,8 @@ public class Builder<T>
return;
}
await fileDao.SetThumbnailStatusAsync(file, Core.Thumbnail.Creating);
if (IsVideo(ext))
{
await MakeThumbnailFromVideo(fileDao, file);
@ -197,14 +199,16 @@ public class Builder<T>
if (IsImage(ext))
{
await CropImage(fileDao, file);
await MakeThumbnailFromImage(fileDao, file);
}
else
{
await MakeThumbnail(fileDao, file);
await MakeThumbnailFromDocs(fileDao, file);
}
}
await fileDao.SetThumbnailStatusAsync(file, Core.Thumbnail.Created);
var newFile = await fileDao.GetFileStableAsync(file.Id);
await _socketManager.UpdateFileAsync(newFile);
@ -244,10 +248,8 @@ public class Builder<T>
File.Delete(tempFilePath);
}
private async Task MakeThumbnail(IFileDao<T> fileDao, File<T> file)
private async Task MakeThumbnailFromDocs(IFileDao<T> fileDao, File<T> file)
{
await fileDao.SetThumbnailStatusAsync(file, Core.Thumbnail.Creating);
foreach (var w in _config.Sizes)
{
_logger.DebugMakeThumbnail1(file.Id.ToString());
@ -261,7 +263,7 @@ public class Builder<T>
try
{
(resultPercent, thumbnailUrl) = await GetThumbnailUrl(file, _global.DocThumbnailExtension.ToString(), w.Width, w.Height);
if (resultPercent == 100)
{
break;
@ -287,7 +289,7 @@ public class Builder<T>
{
_logger.WarningMakeThumbnail(file.Id.ToString(), thumbnailUrl, resultPercent, attempt, exception);
}
}
}
else
{
_logger.WarningMakeThumbnail(file.Id.ToString(), thumbnailUrl, resultPercent, attempt, exception);
@ -310,9 +312,6 @@ public class Builder<T>
await SaveThumbnail(fileDao, file, thumbnailUrl, w.Width, w.Height);
}
await fileDao.SetThumbnailStatusAsync(file, Core.Thumbnail.Created);
}
private async Task<(int, string)> GetThumbnailUrl(File<T> file, string toExtension, int width, int height)
@ -390,7 +389,7 @@ public class Builder<T>
return _fFmpegService.ExistFormat(extention);
}
private async Task CropImage(IFileDao<T> fileDao, File<T> file)
private async Task MakeThumbnailFromImage(IFileDao<T> fileDao, File<T> file)
{
_logger.DebugCropImage(file.Id.ToString());
@ -404,27 +403,11 @@ public class Builder<T>
private async Task Crop(IFileDao<T> fileDao, File<T> file, Stream stream)
{
using (var sourceImg = await Image.LoadAsync(stream))
using var sourceImg = await Image.LoadAsync(stream);
foreach (var w in _config.Sizes)
{
//var tasks = new List<Task>();
//foreach (var w in config.Sizes)
//{
// tasks.Add(CropAsync(sourceImg, fileDao, file, w.Width, w.Height));
//}
//await Task.WhenAll(tasks.ToArray());
//await Parallel.ForEachAsync(config.Sizes, (w, b) => CropAsync(sourceImg, fileDao, file, w.Width, w.Height));
await fileDao.SetThumbnailStatusAsync(file, Core.Thumbnail.Creating);
foreach (var w in _config.Sizes)
{
await CropAsync(sourceImg, fileDao, file, w.Width, w.Height);
}
await fileDao.SetThumbnailStatusAsync(file, Core.Thumbnail.Created);
await CropAsync(sourceImg, fileDao, file, w.Width, w.Height);
}
GC.Collect();
@ -432,7 +415,7 @@ public class Builder<T>
private async ValueTask CropAsync(Image sourceImg, IFileDao<T> fileDao, File<T> file, int width, int height)
{
using var targetImg = GetImageThumbnail(sourceImg, width);
using var targetImg = GetImageThumbnail(sourceImg, width, height);
using var targetStream = new MemoryStream();
switch (_global.ThumbnailExtension)
{
@ -462,11 +445,26 @@ public class Builder<T>
break;
}
await _globalStore.GetStore().SaveAsync(fileDao.GetUniqThumbnailPath(file, width, height), targetStream);
await _globalStore.GetStore().SaveAsync(fileDao.GetUniqThumbnailPath(file, width, height), targetStream);
}
private Image GetImageThumbnail(Image sourceBitmap, int thumbnaillWidth)
private Image GetImageThumbnail(Image sourceBitmap, int thumbnaillWidth, int thumbnaillHeight)
{
return sourceBitmap.Clone(x => x.BackgroundColor(Color.White).Resize(thumbnaillWidth, 0));
return sourceBitmap.Clone(x =>
{
var resizedImage = x.BackgroundColor(Color.White).Resize(thumbnaillWidth, 0);
var resizedImageWidth = resizedImage.GetCurrentSize().Width;
var resizedImageHeight = resizedImage.GetCurrentSize().Height;
var cropWidth = resizedImageWidth < thumbnaillWidth ? resizedImageWidth : thumbnaillWidth;
var cropHeight = resizedImageHeight < thumbnaillHeight ? resizedImageHeight : thumbnaillHeight;
if ((cropHeight != resizedImageHeight) || (cropWidth != resizedImageWidth))
{
x.Crop(cropWidth, cropHeight);
}
});
}
}