Merge pull request #500 from ente-io/handle-blocked-canvas
Handle blocked canvas
This commit is contained in:
commit
a581a4a3cc
|
@ -49,7 +49,7 @@ export default function MessageDialog({
|
|||
{(children || attributes?.content) && (
|
||||
<Modal.Body style={{ borderTop: '1px solid #444' }}>
|
||||
{children || (
|
||||
<p style={{ fontSize: '1.25rem', marginBottom: 0 }}>
|
||||
<p style={{ fontSize: '19px', marginBottom: 0 }}>
|
||||
{attributes.content}
|
||||
</p>
|
||||
)}
|
||||
|
|
|
@ -24,6 +24,8 @@ import { FileUploadResults, UPLOAD_STAGES } from 'constants/upload';
|
|||
import { ElectronFile, FileWithCollection } from 'types/upload';
|
||||
import UploadTypeChoiceModal from './UploadTypeChoiceModal';
|
||||
import Router from 'next/router';
|
||||
import { isCanvasBlocked } from 'utils/upload/isCanvasBlocked';
|
||||
import { downloadApp } from 'utils/common';
|
||||
|
||||
const FIRST_ALBUM_NAME = 'My First Album';
|
||||
|
||||
|
@ -47,6 +49,7 @@ interface Props {
|
|||
setElectronFiles: (files: ElectronFile[]) => void;
|
||||
showUploadTypeChoiceModal: boolean;
|
||||
setShowUploadTypeChoiceModal: (open: boolean) => void;
|
||||
SetDialogMessage: SetDialogMessage;
|
||||
}
|
||||
|
||||
enum UPLOAD_STRATEGY {
|
||||
|
@ -121,32 +124,48 @@ export default function Upload(props: Props) {
|
|||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!props.uploadInProgress &&
|
||||
(props.electronFiles?.length > 0 ||
|
||||
props.droppedFiles?.length > 0 ||
|
||||
appContext.sharedFiles?.length > 0)
|
||||
props.electronFiles?.length > 0 ||
|
||||
props.droppedFiles?.length > 0 ||
|
||||
appContext.sharedFiles?.length > 0
|
||||
) {
|
||||
props.setLoading(true);
|
||||
if (props.droppedFiles?.length > 0) {
|
||||
// File selection by drag and drop or selection of file.
|
||||
toUploadFiles.current = props.droppedFiles;
|
||||
props.clearDroppedFiles();
|
||||
} else if (appContext.sharedFiles?.length > 0) {
|
||||
toUploadFiles.current = appContext.sharedFiles;
|
||||
appContext.resetSharedFiles();
|
||||
} else if (props.electronFiles?.length > 0) {
|
||||
// File selection from desktop app
|
||||
toUploadFiles.current = props.electronFiles;
|
||||
props.setElectronFiles([]);
|
||||
}
|
||||
const analysisResult = analyseUploadFiles();
|
||||
setAnalysisResult(analysisResult);
|
||||
if (props.uploadInProgress) {
|
||||
// no-op
|
||||
// a upload is already in progress
|
||||
} else if (isCanvasBlocked()) {
|
||||
props.setDialogMessage({
|
||||
title: constants.CANVAS_BLOCKED_TITLE,
|
||||
staticBackdrop: true,
|
||||
content: constants.CANVAS_BLOCKED_MESSAGE(),
|
||||
close: { text: constants.CLOSE },
|
||||
proceed: {
|
||||
text: constants.DOWNLOAD_APP,
|
||||
action: downloadApp,
|
||||
variant: 'success',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
props.setLoading(true);
|
||||
if (props.droppedFiles?.length > 0) {
|
||||
// File selection by drag and drop or selection of file.
|
||||
toUploadFiles.current = props.droppedFiles;
|
||||
props.clearDroppedFiles();
|
||||
} else if (appContext.sharedFiles?.length > 0) {
|
||||
toUploadFiles.current = appContext.sharedFiles;
|
||||
appContext.resetSharedFiles();
|
||||
} else if (props.electronFiles?.length > 0) {
|
||||
// File selection from desktop app
|
||||
toUploadFiles.current = props.electronFiles;
|
||||
props.setElectronFiles([]);
|
||||
}
|
||||
const analysisResult = analyseUploadFiles();
|
||||
setAnalysisResult(analysisResult);
|
||||
|
||||
handleCollectionCreationAndUpload(
|
||||
analysisResult,
|
||||
props.isFirstUpload
|
||||
);
|
||||
props.setLoading(false);
|
||||
handleCollectionCreationAndUpload(
|
||||
analysisResult,
|
||||
props.isFirstUpload
|
||||
);
|
||||
props.setLoading(false);
|
||||
}
|
||||
}
|
||||
}, [props.droppedFiles, appContext.sharedFiles, props.electronFiles]);
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Accordion, Button, Modal, ProgressBar } from 'react-bootstrap';
|
|||
import { FileRejection } from 'react-dropzone';
|
||||
|
||||
import styled from 'styled-components';
|
||||
import { DESKTOP_APP_DOWNLOAD_URL } from 'utils/common';
|
||||
import { getOSSpecificDesktopAppDownloadLink } from 'utils/common';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { ButtonVariant, getVariantColor } from './LinkButton';
|
||||
import { FileUploadResults, UPLOAD_STAGES } from 'constants/upload';
|
||||
|
@ -292,7 +292,7 @@ export default function UploadProgress(props: Props) {
|
|||
fileUploadResult={FileUploadResults.BLOCKED}
|
||||
sectionTitle={constants.BLOCKED_UPLOADS}
|
||||
sectionInfo={constants.ETAGS_BLOCKED(
|
||||
DESKTOP_APP_DOWNLOAD_URL
|
||||
getOSSpecificDesktopAppDownloadLink()
|
||||
)}
|
||||
/>
|
||||
<ResultSection
|
||||
|
|
|
@ -668,6 +668,7 @@ export default function Gallery() {
|
|||
setElectronFiles={setElectronFiles}
|
||||
showUploadTypeChoiceModal={showUploadTypeChoiceModal}
|
||||
setShowUploadTypeChoiceModal={setShowUploadTypeChoiceModal}
|
||||
SetDialogMessage={setDialogMessage}
|
||||
/>
|
||||
<Sidebar
|
||||
collections={collections}
|
||||
|
|
|
@ -3,6 +3,9 @@ export enum OS {
|
|||
ANDROID = 'android',
|
||||
IOS = 'ios',
|
||||
UNKNOWN = 'unknown',
|
||||
WINDOWS = 'windows',
|
||||
MAC = 'mac',
|
||||
LINUX = 'linux',
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@ -30,10 +33,22 @@ const GetDeviceOS = () => {
|
|||
}
|
||||
|
||||
// iOS detection from: http://stackoverflow.com/a/9039885/177710
|
||||
if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
|
||||
if (/(iPad|iPhone|iPod)/g.test(userAgent) && !window.MSStream) {
|
||||
return OS.IOS;
|
||||
}
|
||||
|
||||
// credit: https://github.com/MikeKovarik/platform-detect/blob/master/os.mjs
|
||||
if (userAgent.includes('Windows')) {
|
||||
return OS.WINDOWS;
|
||||
}
|
||||
if (userAgent.includes('Macintosh')) {
|
||||
return OS.MAC;
|
||||
}
|
||||
// Linux must be last
|
||||
if (userAgent.includes('Linux')) {
|
||||
return OS.LINUX;
|
||||
}
|
||||
|
||||
return OS.UNKNOWN;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import constants from 'utils/strings/constants';
|
||||
import { CustomError } from 'utils/error';
|
||||
import GetDeviceOS, { OS } from './deviceDetection';
|
||||
|
||||
export const DESKTOP_APP_DOWNLOAD_URL =
|
||||
const DESKTOP_APP_GITHUB_DOWNLOAD_URL =
|
||||
'https://github.com/ente-io/bhari-frame/releases/latest';
|
||||
|
||||
const APP_DOWNLOAD_ENTE_URL_PREFIX = 'https://ente.io/download';
|
||||
|
||||
export function checkConnectivity() {
|
||||
if (navigator.onLine) {
|
||||
return true;
|
||||
|
@ -21,8 +24,21 @@ export async function sleep(time: number) {
|
|||
});
|
||||
}
|
||||
|
||||
export function getOSSpecificDesktopAppDownloadLink() {
|
||||
const os = GetDeviceOS();
|
||||
let url = '';
|
||||
if (os === OS.WINDOWS) {
|
||||
url = `${APP_DOWNLOAD_ENTE_URL_PREFIX}/exe`;
|
||||
} else if (os === OS.MAC) {
|
||||
url = `${APP_DOWNLOAD_ENTE_URL_PREFIX}/dmg`;
|
||||
} else {
|
||||
url = DESKTOP_APP_GITHUB_DOWNLOAD_URL;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
export function downloadApp() {
|
||||
const win = window.open(DESKTOP_APP_DOWNLOAD_URL, '_blank');
|
||||
const link = getOSSpecificDesktopAppDownloadLink();
|
||||
const win = window.open(link, '_blank');
|
||||
win.focus();
|
||||
}
|
||||
|
||||
|
|
|
@ -713,6 +713,19 @@ const englishConstants = {
|
|||
'are you sure that you want to stop all the uploads in progress?',
|
||||
STOP_UPLOADS_HEADER: 'stop uploads?',
|
||||
YES_STOP_UPLOADS: 'yes, stop uploads',
|
||||
CANVAS_BLOCKED_TITLE: 'unable to generate thumbnail',
|
||||
CANVAS_BLOCKED_MESSAGE: () => (
|
||||
<>
|
||||
<p>
|
||||
it looks like your browser has disabled access to canvas, which
|
||||
is necessary to generate thumbnails for your photos
|
||||
</p>
|
||||
<p>
|
||||
please enable access to your browser's canvas, or check out our
|
||||
desktop app
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
};
|
||||
|
||||
export default englishConstants;
|
||||
|
|
53
src/utils/upload/isCanvasBlocked.ts
Normal file
53
src/utils/upload/isCanvasBlocked.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// Canvas Blocker &
|
||||
// Firefox privacy.resistFingerprinting Detector.
|
||||
// (c) 2018 // JOHN OZBAY // CRYPT.EE
|
||||
// MIT License
|
||||
|
||||
// Credits: https://github.com/johnozbay/canvas-block-detector/blob/master/isCanvasBlocked.js
|
||||
|
||||
//
|
||||
export function isCanvasBlocked() {
|
||||
// create a 1px image data
|
||||
let blocked = false;
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// some blockers just return an undefined ctx. So let's check that first.
|
||||
if (ctx) {
|
||||
const imageData = ctx.createImageData(1, 1);
|
||||
const originalImageData = imageData.data;
|
||||
|
||||
// set pixels to RGB 128
|
||||
originalImageData[0] = 128;
|
||||
originalImageData[1] = 128;
|
||||
originalImageData[2] = 128;
|
||||
originalImageData[3] = 255;
|
||||
|
||||
// set this to canvas
|
||||
ctx.putImageData(imageData, 1, 1);
|
||||
|
||||
try {
|
||||
// now get the data back from canvas.
|
||||
const checkData = ctx.getImageData(1, 1, 1, 1).data;
|
||||
|
||||
// If this is firefox, and privacy.resistFingerprinting is enabled,
|
||||
// OR a browser extension blocking the canvas,
|
||||
// This will return RGB all white (255,255,255) instead of the (128,128,128) we put.
|
||||
|
||||
// so let's check the R and G to see if they're 255 or 128 (matching what we've initially set)
|
||||
if (
|
||||
originalImageData[0] !== checkData[0] &&
|
||||
originalImageData[1] !== checkData[1]
|
||||
) {
|
||||
blocked = true;
|
||||
}
|
||||
} catch (error) {
|
||||
// some extensions will return getImageData null. this is to account for that.
|
||||
blocked = true;
|
||||
}
|
||||
} else {
|
||||
blocked = true;
|
||||
}
|
||||
return blocked;
|
||||
}
|
Loading…
Reference in a new issue